mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1256545: avoid compiler warning for pointer truncation when checking low bits r=bwc (d16c97ba81)
- Bug 1255655 - Const-ify and shrink octet_weight. r=hurley. (d5b4c89e87)
- Bug 1216837: add explicit error checks for packet length in srtp r=mcmanus rs=jesup (ce11d6694e)
- Bug 1248565 - Let child processes have its own MOZ_LOG_FILE. r=erahm (a242c8fa08)
- Bug 1256558 - Change MUST_CONVERT to avoid C4311 in VS2015; r=khuey (81b1f997f6)
- Bug 580313 - Use deque instead of manual queue im nsPrefetchService. r=smaug (023e0115e8)
- Bug 580313 - New resource hints for link. r=smaug (3c7949ab9a)
- Bug 1253593 - Fix applicationCache.onchecking notification on e10s. r=jduell (debb998b9d)
- Bug 1234177 - check to see if mFunctions.append returned error. r=bholley (0dc9d44233)
- Bug 1183754, part 1 - Get rid of aligned spacing for XPCWrappedNative fields. r=bholley (dfb09ad1a4)
- Bug 1183754, part 2 - Remove clearing of the next chunk. r=bholley (78e8c4d7cb)
- Bug 1183754, part 3 - Use a UniquePtr for XPCWrappedNativeChunk::mNextChunk. r=bholley (26d816ff0c)
- Bug 1183754, part 4 - Eliminate XPCWrappedNativeTearOffChunk. r=bholley (0f50e6c9ff)
- Bug 1252154: Delay allocation metadata collection for inline typed objects. r=sfink (9f3cea8d76)
- Bug 1250195: In TypedObject.from, decide that the input is typed only if it's an array type; r=pnkfelix (2ceb08c793)
- Bug 1250842 - Fix initialization of script source object when modules are compiled off main thread r=shu (ca3f9b900e)
- Bug 1257053 - Only make use of error stashing (via PossibleError) when it is necessary; r=jorendorff (f3744899c4)
- Bug 1253847 - Do not count the skipped EOL inside template literal. r=jorendorff (f63926bb2d)
- Bug 1221378: Don't collect allocation metadata when lazily creating standard prototypes. r=fitzgen (667e752baf)
- Bug 1249469 - Allow any script to execute when resolving constructors. (r=jimb) (474fae3645)
- Bug 1221378: SpiderMonkey: Assert against re-entrant constructor resolution. r=fitzgen (15619325de)
- Bug 1249469 - Followup: missing #include on a CLOSED TREE. (3c62bbbb31)
- Bug 1248412 - inlineIsTypedArrayHelper: Check for TypedArray and Proxy classes when we allow wrapped TypedArray. r=Waldo (1bcaba804b)
- Bug 1250307 - Add the by: "bucket" breakdown option; r=jimb (8ca32dc781)
- Bug 1221378: Add JSCLASS_DELAY_METADATA_CALLBACK flag to UnboxedPlainObject. r=jandem (7e0eb0be01)
- Bug 1064543 - Don't emit FilterTypeSet if it wouldn't remove any types. r=h4writer (d1a1a4e127)
- Bug 1242462 - Add a newline when calling TypeSet::print from a debugger. r=jandem (2e4c07aaf3)
- Bug 1247140 - Use mozilla::BinarySearch{,If} for more manual binary searches in SpiderMonkey. r=jandem (b948fe7f60)
- Bug 1249193 - Fix Debugger.Frame.this to work correctly if we're still in the script's prologue. r=shu (0f17a78c1a)
- Bug 1249193 part 2 - Fix DebugScopeProxy to return correct this-value if we're still in the prologue. r=shu (5e17bdb2c9)
- Bug 1187450 - avoid leaking cstr in SPSProfiler::allocProfileString. r=jorendorff (96964744b2)
- Bug 1251790 - Add help for "interface objects", r=terrence (9a5d8cc3fa)
- Bug 944164 - Implement proper redirection with ref counted underlying files, r=terrence (f4182fff75)
- Bug 1248352 - Allow shell option parsing code to handle help text containing blank lines r=jandem (d7f2814665)
- Bug 1249954 - Handle OOM in SingleStepCallback. r=terrence (460f323729)
- Bug 1257223 - Fix os.file.redirect(null), r=jonco (2ca40fd839)
- Bug 1245310 - Restore null check in GetFocusedNode(). r=khuey (fa816d87b2)
- Bug 1256424. Get rid of ThreadsafeAutoSafeJSContext. r=bholley (445aa7dd4b)
- Bug 1256424 followup to actually address the review comments (d085cf1900)
- Bug 1003432: Expose CustomEvent in Worker. r=smaug (3b143dc6e4)
- Bug 1256414 - Hide MozSettingsEvent from the Web; r=khuey (52fe18e823)
- Bug 1257038: Remove the worker descriptor for WorkerLocation. r=bz (2b9721e4fe)
- Bug 1257039: Remove the worker descriptor for FileReaderSync. r=bz (cc33ce76e6)
- Bug 1257355: Remove the worker descriptor for WorkerNavigator. r=bz (067f1fc9ea)
- Bug 1257480 - null check for GetOrCreateGlobalScope() in WorkerDebuggerGlobalScope, r=khuey (0d3d640fc0)
- Make split-profile run jprof in the directory with the jprof-log. No bug. (58f5060e39)
- Bug 1250333 - do not create accessibles for trailing BRs, r=davidb, roc (d8e1193adc)
- Bug 1251712 - propagate a context flag for alerts, r=davdib (d5ef6167ea)
- Bug 1251752 - logging: add tree specific methods, r=yzen (f974c207ca)
- Bug 1251218 - add special TreeWalker constructor for children creation, r=marcoz (74f939b93e)
- Bug 1251337 - TreeWalker doesn't have to check ARIA owned children for each DOM state, r=yzen (19d83af7e1)
- Bug 1249730 - make TreeWalker bi-directional, r=yzen (a229a591e1)
- Bug 1249730 - make TreeWalker bi-directional, r=yzen (5acc1155b1)
- Bug 1251743 - ARIA owns reallocation may insert a child at wrong index, r=yzen (d95065109b)
- Bug 1249253 - content removal processing can wrongly remove ARIA owned children, r=yzen (45df52f4c9)
- Bug 1254989 - extend TreeWalker::Next to accept a stopper node, r=yzen (c5dfbbdc96)
- Bug 1249730 - make TreeWalker bi-directional, follow up fix, r=yzen (747504b5ec)
- Bug 1196652 - OriginSuffix is shown in about:serviceworker on b2g. r=ferjm (f21d534755)
- Bug 1224570 - [Service Workers Panel] Service Workers panel fails to show registered service workers after restart. r=fabrice (7d7e95db4c)
- Bug 1169674 - Use originNoSuffix for permission event. r=fabrice, f=bholley (8f389cb0c7)
- Bug 1207499 - Part 1: Remove use of expression closure from b2g/. r=sicking (42b96cbfdf)
- Bug 1207499 - Part 2: Remove use of expression closure from chrome/. r=bsmedberg (7abb390349)
- Bug 1207499 - Part 3: Remove use of expression closure from docshell/. r=bz (aee60733a9)
- Bug 1207499 - Part 5: Remove use of expression closure from modules/. r=mwu (6c29c775fe)
- Bug 1147562 - Update remaining callsites of newChannel before landing the shim in netwerk/ (r=jduell) (2d37fab088)
- Bug 1207499 - Part 6: Remove use of expression closure from netwerk/. r=jduell (2881b3450f)
- Bug 1207499 - Part 7: Remove use of expression closure from parser/. r=jst (2519fb1fff)
- Bug 1229913 - Prevent race conditions when there are many animation mutations; r=miker (442a1af761)
- Bug 1208204 - Pressing space in the animation inspector should toggle play/pause. r=pbrosset (f15cb19dcb)
- Bug 1249719 - Use promise.all instead of Promise.all to release player fronts; r=ochameau (560b1de176)
- Bug 1207499 - Part 8: Remove use of expression closure from security/. r=keeler (de33d27e8f)
- Bug 1207499 - Part 12: Remove use of expression closure from widget/. r=roc (0e7fd889d9)
- Bug 1207499 - Part 13: Remove use of expression closure from xpcom/. r=froydnj (f95a4eefde)
- Bug 1213680 - Stop using picker by default when opening inspector (r=pbrosset) (64c2741a0b)
- Bug 1233497 - Avoid unsafe CPOWs in devtools tests. r=jryans (d8bdcac868)
- Bug 1059312 - Fix highlighter offset after switching iframe context; =pbro (da36ee302d)
- Bug 1188695 - Increase the timeout of browser_markupview_keybindings_04.js (d94bafb1ef)
- Bug 1199180 - Wait for the inspector-updated event after selecting nodes with UP key. r=pbrosset (3ffcead1cd)
- Bug 1245654 - Make the inspector shortcut start the pick mode again; r=bgrins (f30b156b44)
- Bug 1244120 - Enable browser_rules_content_02.js with e10s; r=bgrins (94f9e97f20)
- Bug 1208864 - Duplicate node context menu item in markup view. r=pbro (1044d79738)
- Bug 1233363 - Fix timelime typo in animationinspector.css; r=me (0e4338c17e)
- Bug 1229000 - Adds separator borders in the timeline toolbar; r=miker (f0c07b9555)
- Bug 1233367 - Right-border of current time label has wrong color with the dark-theme .r=pbrosset (9017ea3e21)
- Bug 1239298 - Make sure percentage width of iterations and delays in the timeline are relative to the same parent (c900c4f4eb)
- Bug 1221494 - Fix the playback rate selector size on Linux; r=pbro (ddcd686764)
- Bug 1245276 - Make 'All animations' left border darker in dark theme. r=bgrins (d6b9edbda9)
- Bug 1228978 - Add a drop-mark to the playback-rate selector in the animation-inspector's toolbar; r=pbro (1948d0aeb4)
- Bug 1231945 - Display animation.id when it exists; r=tromey (291203c9a4)
- Bug 1231688 - Use waapi computed timing in devtools animation actors; r=tromey (4a6fd44649)
- Bug 1245849 - Remove mochitest browser_animation_name.js and add a xpcshell test instead; r=ochameau (bb1a570057)
- Bug 1232681 - Display script-generated animations correctly. r=pbro (15501a8ccb)
- Bug 1199712 - Prevent the animation panel from scrolling when space is pressed; r=pbro (c291c8541f)
- Bug 1247243 - Animations are shown only every 2 reloads. r=pbrosset (6162103af6)
- Bug 1249219 - Part 1: Define NonOwningAnimationTarget. r=birtles (c13d77b5d9)
- Bug 1249219 - Part 2: Remove struct PseudoElementHashKey. r=birtles (f8ff47d484)
- Bug 1249219 - Part 3: Replace Pair<Element*, CSSPseudoElementType> with NonOwningAnimationTarget. r=birtles (68a1a5e149)
- Bug 1218620 - Allow opacity animation running on compositor even if the frame has any restricted transforms. r=birtles (0f26c81fb0)
- Bug 1254419 - Move GetPropertyState alongside GetFrames; r=hiro (046dbce30e)
- Bug 1246320 part 0 - Whitespace fixes; r=whitespace-only (eda3e8b8ae)
- Bug 1247531 - Annotate intentional switch fallthrough to suppress -Wimplicit-fallthrough warning in dom/animations/. r=dholbert (fbdff98c25)
- Bug 1246320 part 1 - Add AnimationUtils::GetCurrentRealmDocument; r=bz (53c52acbe7)
- Bug 1246320 part 2 - Pass document to ParseEasing; r=hiro (269325c142)
- Bug 1246320 part 3 - Rework KeyframeEffect(ReadOnly) constructor helpers; r=hiro (e0c58fbe49)
- Bug 1246320 part 4 - Pass a document to TimingParams; r=hiro (f9ef7bc956)
- Bug 1246320 part 5 - Simplify KeyframeEffect(ReadOnly) Constructor overloads further; r=hiro (ec932de9a7)
- Bug 1254419 - Rename getPropertyState() to getProperties(); r=heycam, r=bz (4ab86c889d)
- Bug 1254419 - Fix zero-length segment handling; r=heycam (a638e5bbd7)
- Bug 1254419 - Fill in values sequence in getProperties(); r=heycam (c7c233f5ce)
- Bug 1254419 - Return animation property information from getProperties() even if the property is overridden; r=hiro (55537b4445)
- Bug 1254419 - Add values member to AnimationPropertyDetails; r=heycam, r=bz (3ea6c1fb7f)
- Bug 1254419 - Add tests for getProperties(); r=heycam (ff9999286d)
- Bug 1254419 - Make always-set members of AnimationProperty(Value)Details required; r=bz (07ef47d79a)
- Bug 1254419 - Throw if we fail to allocate memory for a values array in getProperties(); r=bz (51ae5d9f99)
- Bug 1003204: Removed CommonUtils.exceptionStr() in toolkit/ r=gfritzsche (d1c2efa08f)
- Bug 1243936 - Convert remaining callsites within devtools/ and toolkit/ to use channel.open2() (54f398eb47)
- Bug 1187270 - Add Telemetry session ID to crash annotations, r=gfritzsche (6c08170c5a)
- Bug 1249219 - Part 4: Use NonOwningAnimationTarget as the returned value of some animation target getters. r=birtles (df788abe39)
- Bug 1249219 - Part 5: Add a wrapper of AnimationAdded/Changed/Removed. r=birtles (58cf3a3ce2)
- Bug 1249219 - Part 6: Support pseudo elements in Animation Mutation Observer. r=heycam (d10c901821)
- Bug 1249219 - Part 7: Test. r=birtles (e2b78422be)
- Bug 1249219 - Part 8: Avoid adding animations on pseudo elements for Inspector temporary. r=pbro (2bff38bfe1)
- add back some utils of Bug 751291 used in tests (8fd2cc847f)
- Bug 1253470 - Part 1: Produce console warnings for invalid duration. r=birtles (a0491eeab4)
- Bug 1253470 - Part 2: Produce console warnings for invalid iterationStart. r=birtles (6a910650c9)
- Bug 1253470 - Part 3: Produce console warnings for invalid iterations. r=birtles (e3210e754e)
- Bug 1253470 - Part 4: Produce console warnings for invalid easing. r=birtles (fc1868b3c0)
- Bug 1245748 - Move ComputedTiming to a separate file; r=heycam (22a76e4f03)
- Bug 1245748 - Rename Keyframe-related IDL types to match changes to Web Animations spec; r=heycam, r=bz (e79338bafd)
- Bug 1245748 - Update handling of 'composite' dictionary members to match changes to the spec; r=heycam, r=bz (d9cc71cde8)
- Bug 1245748 - Define the Keyframe type for storing specified keyframes; r=heycam (a429e2bf46)
- Bug 1245748 - Add missing includes to TimingParams.{cpp,h}; r=heycam (3e1e121c6f)
- Bug 1245748 - Move keyframe handling code to a separate KeyframeUtils class; r=heycam (e359f26244)
- Bug 1245748 - Add KeyframeUtils::GetKeyframesFromObject; r=heycam (eda69445d7)
- Bug 1245748 - Add nsStyleContext parameter to StyleAnimationValue::ComputeValue(s); r=heycam (2c22b9926c)
- Bug 1245748 - Add a variant of StyleAnimationValue::ComputeValues that takes an nsCSSValue; r=heycam (12386559dd)
- Bug 1245748 - Split PropertyPriorityComparator into a separate (reusable) class; r=heycam (132394bf45)
- Bug 1245748 - Add PropertyPriorityIterator; r=heycam (bfef46fd12)
- Bug 1245748 - Add GetAnimationPropertiesFromKeyframes; r=heycam (4681ac8407)
- Bug 1245748 - Add ApplyDistributeSpacing for Keyframe objects; r=heycam (9c0bc885c9)
- Bug 1245748 - Use Keyframe-based utility functions when constructing KeyframeEffect(ReadOnly); r=heycam (e0b7460548)
- Bug 1229859 - Introduce new import-globals-from eslint rule to import globals from other modules; r=Mossop (10075a136c)
- Bug 1227477 - Polish the way the timeline time graduations are calculated; r=pbro (eb383aa4a0)
- Bug 1219611 - When animations end in the timeline, make sure the time-label shows the right time; r=pbro (518b574e74)
- Bug 1242898 - Clean devtools/client/animationinspector of eslint errors; r=me (df8451951b)
- Bug 1203398 - Increase the timeout of browser_animation_empty_on_invalid_nodes.js (c339d66030)
- Bug 1222937 - Show a dedicated error message for animated pseudo elements; r=pbro (b0858c955c)
- Bug 1231362 - Part 1: Upgrade existing addons to CodeMirror 5.9.0 r=bgrins (626a3129ee)
- Bug 1231362 - Part 2: Upgrade existing CodeMirror keymap to 5.9.0 r=bgrins (43f135e34b)
- Bug 1231362 - Part 3: Upgrade to CodeMirror 5.9.0 r=bgrins (e14212f0b1)
- Bug 1231362 - Part 4: Upgrade CodeMirror mode to 5.9.0 r=bgrins (049181c2b7)
- Bug 1231362 - Part 5: Upgrade existing CodeMirror tests to 5.9.0 r=bgrins (15ce59f2cc)
- Bug 1231362 - Part 6: Apply CodeMirror customization for search.js r=bgrins (a326658816)
- Bug 1231362 - Part 7: Update CodeMirror README for version 5.9.0 r=bgrins (13eca0cd85)
- Bug 1234374 - Part 1: Upgrade existing addons to CodeMirror 5.10.0 r=bgrins (0cf4d63559)
- Bug 1234374 - Part 2: Upgrade existing CodeMirror keymap to 5.10.0 r=grins (47bdb7b9b1)
- Bug 1234374 - Part 3: Upgrade to CodeMirror 5.10.0 r=bgrins (0c20797954)
- Bug 1234374 - Part 4: Upgrade CodeMirror mode to 5.10.0 r=bgrins (9678c17894)
- Bug 1234374 - Part 5: Upgrade existing CodeMirror tests to 5.10.0 r=bgrins (477daf4c8b)
- Bug 1234374 - Part 6: Update CodeMirror README for version 5.10.0 r=bgrins (dacf42a87b)
- Bug 1242309 - Upgrade to CodeMirror 5.11.0 r=bgrins (e99b62fdc7)
- Bug 762979 - Implement shorlander's line gutter mockup for the source editor. r=vporof, bgrins (a23de53dbe)
- Bug 762979 - Implement conditional breakpoint gutter style. r=jlongster (dbbfed7ef9)
- Bug 1245030 - simplify breakpoint colors in debugger to reduce visual clutter r=hholmes (643e596727)
- reshuffle modules a little (69d9409a23)
- Bug 1238537 - make nsBrowserContentHandler.js use Services.jsm, r=florian.dapted since we still have Preference Windows (136514e705)
- fix exception (ccbcb581e1)
- Bug 436564 - Add unit tests for distribution.js; r=mixedpuppy (aa3474f7ed)
- Bug 1249630 - Add language support to distribution.js; r=mixedpuppy (cfea768ef2)
- Bug 1249742 - Don't set prefs at all if overridden by lang or locale; r=mixedpuppy (3a94e1aff6)
- Bug 1251729 - Don't use eval for distribution.js; r=mixedpuppy (b2e199f68b)
- Bug 1252466 - Switch distribution.js to use Preferences.jsm; r=mixedpuppy (9ce63143ce)
- Bug 782924 - Allow locale specific preferences in distribution.ini; r=mixedpuppy (016c42ab53)
- Bug 1252627 - distribution defaultLocale shoudld take precedence over user agent locale; r=mixedpuppy (d154458567)
- Bug 1248829 - Fix number of characters reserved in memory table's cells; r=jsantell (052b7d7ca3)
- Bug 1242296 - mem. profiler: clear snapshots icon; r=ntim (2f7b316334)
- Bug 1224660 - New icon for snapshot diff view button. r=ntim (900c618352)
- Bug 1219584 - Test that we show the allocations recording hint at the correct times. r=jsantell (5af9ca4486)
- Bug 1149385 - Render retaining paths in the dominators view; r=jsantell (bd05c3a138)
- Bug 1171903 - Add endless scrolling to storage inspector to view more items. r=miker (7c9aed5f3a)
- Bug 1003860 - Various tweaks to the storage inspector. r=mratcliffe (9badb236c9)
- Bug 1171903 - Optimise storage inspector CSS and disable animations for tests. r=miker (4297543cf2)
- Bug 1248563 - eslint cleanup of storage inspector code r=pbrosset (0d9f0219c8)
- Bug 1224115 - Allow filtering the storage inspector table. r=mratcliffe (21be5a691e)
- Bug 1224115 - Properly refresh zebra stripes in the table. r=mratcliffe (6a52bd7de1)
- Bug 1253072 - Small bug fixes to the Storage Inspector functionality. r=miker (74e5bd52c6)
- Bug 1140839 - Scratchpad should remember View options. r=past (6da9c4d13f)
- Bug 1253125 - Stop duplicating telemetry from inside tools. r=mratcliffe (736ab1872f)
- Bug 1250833 - Stop using browser.xul globals specific to devtools. r=jryans (956939f560)
- Bug 1167478 - Menu items "Larger Font" and "Smaller Font" should be disabled if the current font size is equals to MAXIMUM_FONT_SIZE or MINIMUM_FONT_SIZE constants. r=past (ececdb4ac1)
- cleanup (447f001158)
- Bug 1246273 - Add localized strings for types and attempts in the JIT opts view. r=vp (a9875992a5)
This commit is contained in:
@@ -85,7 +85,6 @@ browser/extensions/loop/**
|
||||
|
||||
# devtools/ exclusions
|
||||
devtools/*.js
|
||||
devtools/client/animationinspector/**
|
||||
devtools/client/canvasdebugger/**
|
||||
devtools/client/commandline/**
|
||||
devtools/client/debugger/**
|
||||
|
||||
@@ -44,7 +44,7 @@ static const nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
kGenericAccType,
|
||||
eAlert,
|
||||
kNoReqStates
|
||||
},
|
||||
{ // alertdialog
|
||||
|
||||
+15
-14
@@ -68,20 +68,21 @@ enum AccType {
|
||||
* type, the same accessible class can have several types.
|
||||
*/
|
||||
enum AccGenericType {
|
||||
eAutoComplete = 1 << 0,
|
||||
eAutoCompletePopup = 1 << 1,
|
||||
eButton = 1 << 2,
|
||||
eCombobox = 1 << 3,
|
||||
eDocument = 1 << 4,
|
||||
eHyperText = 1 << 5,
|
||||
eLandmark = 1 << 6,
|
||||
eList = 1 << 7,
|
||||
eListControl = 1 << 8,
|
||||
eMenuButton = 1 << 9,
|
||||
eSelect = 1 << 10,
|
||||
eTable = 1 << 11,
|
||||
eTableCell = 1 << 12,
|
||||
eTableRow = 1 << 13,
|
||||
eAlert = 1 << 0,
|
||||
eAutoComplete = 1 << 1,
|
||||
eAutoCompletePopup = 1 << 2,
|
||||
eButton = 1 << 3,
|
||||
eCombobox = 1 << 4,
|
||||
eDocument = 1 << 5,
|
||||
eHyperText = 1 << 6,
|
||||
eLandmark = 1 << 7,
|
||||
eList = 1 << 8,
|
||||
eListControl = 1 << 9,
|
||||
eMenuButton = 1 << 10,
|
||||
eSelect = 1 << 11,
|
||||
eTable = 1 << 12,
|
||||
eTableCell = 1 << 13,
|
||||
eTableRow = 1 << 14,
|
||||
|
||||
eLastAccGenericType = eTableRow
|
||||
};
|
||||
|
||||
+121
-2
@@ -45,14 +45,16 @@ static ModuleRep sModuleMap[] = {
|
||||
|
||||
{ "events", logging::eEvents },
|
||||
{ "platforms", logging::ePlatforms },
|
||||
{ "stack", logging::eStack },
|
||||
{ "text", logging::eText },
|
||||
{ "tree", logging::eTree },
|
||||
|
||||
{ "DOMEvents", logging::eDOMEvents },
|
||||
{ "focus", logging::eFocus },
|
||||
{ "selection", logging::eSelection },
|
||||
{ "notifications", logging::eNotifications }
|
||||
{ "notifications", logging::eNotifications },
|
||||
|
||||
{ "stack", logging::eStack },
|
||||
{ "verbose", logging::eVerbose }
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -610,6 +612,61 @@ logging::SelChange(nsISelection* aSelection, DocAccessible* aDocument,
|
||||
Stack();
|
||||
}
|
||||
|
||||
void
|
||||
logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...)
|
||||
{
|
||||
if (IsEnabledAll(logging::eTree | aExtraFlags)) {
|
||||
MsgBegin("TREE", aMsg);
|
||||
|
||||
va_list vl;
|
||||
va_start(vl, aExtraFlags);
|
||||
const char* descr = nullptr;
|
||||
while ((descr = va_arg(vl, const char*))) {
|
||||
AccessibleInfo(descr, va_arg(vl, Accessible*));
|
||||
}
|
||||
va_end(vl);
|
||||
|
||||
MsgEnd();
|
||||
|
||||
if (aExtraFlags & eStack) {
|
||||
Stack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags,
|
||||
const char* aMsg1, Accessible* aAcc,
|
||||
const char* aMsg2, nsINode* aNode)
|
||||
{
|
||||
if (IsEnabledAll(logging::eTree | logging::eVerbose)) {
|
||||
MsgBegin("TREE", aMsg);
|
||||
AccessibleInfo(aMsg1, aAcc);
|
||||
Accessible* acc = aAcc->Document()->GetAccessible(aNode);
|
||||
if (acc) {
|
||||
AccessibleInfo(aMsg2, acc);
|
||||
}
|
||||
else {
|
||||
Node(aMsg2, aNode);
|
||||
}
|
||||
MsgEnd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent)
|
||||
{
|
||||
if (IsEnabledAll(logging::eTree | aExtraFlags)) {
|
||||
MsgBegin("TREE", aMsg);
|
||||
AccessibleInfo("container", aParent);
|
||||
for (uint32_t idx = 0; idx < aParent->ChildCount(); idx++) {
|
||||
AccessibleInfo("child", aParent->GetChildAt(idx));
|
||||
}
|
||||
MsgEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
logging::MsgBegin(const char* aTitle, const char* aMsgText, ...)
|
||||
{
|
||||
@@ -740,6 +797,62 @@ logging::Document(DocAccessible* aDocument)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
logging::AccessibleInfo(const char* aDescr, Accessible* aAccessible)
|
||||
{
|
||||
printf(" %s: %p; ", aDescr, static_cast<void*>(aAccessible));
|
||||
if (!aAccessible) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
if (aAccessible->IsDefunct()) {
|
||||
printf("defunct\n");
|
||||
return;
|
||||
}
|
||||
if (!aAccessible->Document() || aAccessible->Document()->IsDefunct()) {
|
||||
printf("document is shutting down, no info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString role;
|
||||
GetAccService()->GetStringRole(aAccessible->Role(), role);
|
||||
printf("role: %s", NS_ConvertUTF16toUTF8(role).get());
|
||||
|
||||
nsAutoString name;
|
||||
aAccessible->Name(name);
|
||||
if (!name.IsEmpty()) {
|
||||
printf(", name: '%s'", NS_ConvertUTF16toUTF8(name).get());
|
||||
}
|
||||
|
||||
printf(", idx: %d", aAccessible->IndexInParent());
|
||||
|
||||
nsINode* node = aAccessible->GetNode();
|
||||
if (!node) {
|
||||
printf(", node: null\n");
|
||||
}
|
||||
else if (node->IsNodeOfType(nsINode::eDOCUMENT)) {
|
||||
printf(", document node: %p\n", static_cast<void*>(node));
|
||||
}
|
||||
else if (node->IsNodeOfType(nsINode::eTEXT)) {
|
||||
printf(", text node: %p\n", static_cast<void*>(node));
|
||||
}
|
||||
else if (node->IsElement()) {
|
||||
dom::Element* el = node->AsElement();
|
||||
|
||||
nsAutoCString tag;
|
||||
el->NodeInfo()->NameAtom()->ToUTF8String(tag);
|
||||
|
||||
nsIAtom* idAtom = el->GetID();
|
||||
nsAutoCString id;
|
||||
if (idAtom) {
|
||||
idAtom->ToUTF8String(id);
|
||||
}
|
||||
|
||||
printf(", element node: %p, %s@id='%s'\n",
|
||||
static_cast<void*>(el), tag.get(), id.get());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
logging::AccessibleNNode(const char* aDescr, Accessible* aAccessible)
|
||||
{
|
||||
@@ -818,6 +931,12 @@ logging::IsEnabled(uint32_t aModules)
|
||||
return sModules & aModules;
|
||||
}
|
||||
|
||||
bool
|
||||
logging::IsEnabledAll(uint32_t aModules)
|
||||
{
|
||||
return (sModules & aModules) == aModules;
|
||||
}
|
||||
|
||||
bool
|
||||
logging::IsEnabled(const nsAString& aModuleStr)
|
||||
{
|
||||
|
||||
@@ -35,14 +35,17 @@ enum EModules {
|
||||
|
||||
eEvents = 1 << 3,
|
||||
ePlatforms = 1 << 4,
|
||||
eStack = 1 << 5,
|
||||
eText = 1 << 6,
|
||||
eTree = 1 << 7,
|
||||
eText = 1 << 5,
|
||||
eTree = 1 << 6,
|
||||
|
||||
eDOMEvents = 1 << 8,
|
||||
eFocus = 1 << 9,
|
||||
eSelection = 1 << 10,
|
||||
eNotifications = eDOMEvents | eSelection | eFocus
|
||||
eDOMEvents = 1 << 7,
|
||||
eFocus = 1 << 8,
|
||||
eSelection = 1 << 9,
|
||||
eNotifications = eDOMEvents | eSelection | eFocus,
|
||||
|
||||
// extras
|
||||
eStack = 1 << 10,
|
||||
eVerbose = 1 << 11
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -50,6 +53,11 @@ enum EModules {
|
||||
*/
|
||||
bool IsEnabled(uint32_t aModules);
|
||||
|
||||
/**
|
||||
* Return true if all of the given modules are logged.
|
||||
*/
|
||||
bool IsEnabledAll(uint32_t aModules);
|
||||
|
||||
/**
|
||||
* Return true if the given module is logged.
|
||||
*/
|
||||
@@ -121,6 +129,15 @@ void FocusDispatched(Accessible* aTarget);
|
||||
void SelChange(nsISelection* aSelection, DocAccessible* aDocument,
|
||||
int16_t aReason);
|
||||
|
||||
/**
|
||||
* Log the given accessible elements info.
|
||||
*/
|
||||
void TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...);
|
||||
void TreeInfo(const char* aMsg, uint32_t aExtraFlags,
|
||||
const char* aMsg1, Accessible* aAcc,
|
||||
const char* aMsg2, nsINode* aNode);
|
||||
void TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent);
|
||||
|
||||
/**
|
||||
* Log the message ('title: text' format) on new line. Print the start and end
|
||||
* boundaries of the message body designated by '{' and '}' (2 spaces indent for
|
||||
@@ -164,6 +181,7 @@ void Document(DocAccessible* aDocument);
|
||||
/**
|
||||
* Log the accessible and its DOM node as a message entry.
|
||||
*/
|
||||
void AccessibleInfo(const char* aDescr, Accessible* aAccessible);
|
||||
void AccessibleNNode(const char* aDescr, Accessible* aAccessible);
|
||||
void AccessibleNNode(const char* aDescr, nsINode* aNode);
|
||||
|
||||
@@ -189,7 +207,8 @@ void Enable(const nsAFlatCString& aModules);
|
||||
*/
|
||||
void CheckEnv();
|
||||
|
||||
} // namespace logs
|
||||
} // namespace logging
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
+223
-69
@@ -21,18 +21,33 @@ using namespace mozilla::a11y;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TreeWalker::
|
||||
TreeWalker(Accessible* aContext, nsIContent* aContent, uint32_t aFlags) :
|
||||
mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aContent),
|
||||
mFlags(aFlags)
|
||||
TreeWalker(Accessible* aContext) :
|
||||
mDoc(aContext->Document()), mContext(aContext), mAnchorNode(nullptr),
|
||||
mARIAOwnsIdx(0),
|
||||
mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(0),
|
||||
mPhase(eAtStart)
|
||||
{
|
||||
NS_ASSERTION(aContent, "No node for the accessible tree walker!");
|
||||
|
||||
mChildFilter = mContext->NoXBLKids() ?
|
||||
mChildFilter |= mContext->NoXBLKids() ?
|
||||
nsIContent::eAllButXBL : nsIContent::eAllChildren;
|
||||
mChildFilter |= nsIContent::eSkipPlaceholderContent;
|
||||
|
||||
if (aContent)
|
||||
PushState(aContent);
|
||||
mAnchorNode = mContext->IsDoc() ?
|
||||
mDoc->DocumentNode()->GetRootElement() : mContext->GetContent();
|
||||
|
||||
MOZ_COUNT_CTOR(TreeWalker);
|
||||
}
|
||||
|
||||
TreeWalker::
|
||||
TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags) :
|
||||
mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aAnchorNode),
|
||||
mARIAOwnsIdx(0),
|
||||
mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(aFlags),
|
||||
mPhase(eAtStart)
|
||||
{
|
||||
MOZ_ASSERT(mFlags & eWalkCache, "This constructor cannot be used for tree creation");
|
||||
MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
|
||||
|
||||
mChildFilter |= mContext->NoXBLKids() ?
|
||||
nsIContent::eAllButXBL : nsIContent::eAllChildren;
|
||||
|
||||
MOZ_COUNT_CTOR(TreeWalker);
|
||||
}
|
||||
@@ -42,35 +57,122 @@ TreeWalker::~TreeWalker()
|
||||
MOZ_COUNT_DTOR(TreeWalker);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TreeWalker: private
|
||||
|
||||
Accessible*
|
||||
TreeWalker::Next()
|
||||
bool
|
||||
TreeWalker::Seek(nsIContent* aChildNode)
|
||||
{
|
||||
if (mStateStack.IsEmpty())
|
||||
return nullptr;
|
||||
MOZ_ASSERT(aChildNode, "Child cannot be null");
|
||||
|
||||
ChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
|
||||
while (top) {
|
||||
Accessible* child = nullptr;
|
||||
bool skipSubtree = false;
|
||||
while (nsIContent* childNode = Next(top, &child, &skipSubtree)) {
|
||||
if (child)
|
||||
return child;
|
||||
mPhase = eAtStart;
|
||||
mStateStack.Clear();
|
||||
mARIAOwnsIdx = 0;
|
||||
|
||||
// Walk down into subtree to find accessibles.
|
||||
if (!skipSubtree && childNode->IsElement())
|
||||
top = PushState(childNode);
|
||||
nsIContent* childNode = nullptr;
|
||||
nsINode* parentNode = aChildNode;
|
||||
do {
|
||||
childNode = parentNode->AsContent();
|
||||
parentNode = childNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
|
||||
(mChildFilter & nsIContent::eAllButXBL) ?
|
||||
childNode->GetParentNode() : childNode->GetFlattenedTreeParent();
|
||||
|
||||
if (!parentNode || !parentNode->IsElement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If ARIA owned child.
|
||||
Accessible* child = mDoc->GetAccessible(childNode);
|
||||
if (child && child->IsRelocated()) {
|
||||
if (child->Parent() != mContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Accessible* ownedChild = nullptr;
|
||||
while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) &&
|
||||
ownedChild != child);
|
||||
|
||||
MOZ_ASSERT(ownedChild, "A child has to be in ARIA owned elements");
|
||||
mPhase = eAtARIAOwns;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look in DOM.
|
||||
dom::AllChildrenIterator* iter = PrependState(parentNode->AsElement(), true);
|
||||
if (!iter->Seek(childNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parentNode == mAnchorNode) {
|
||||
mPhase = eAtDOM;
|
||||
return true;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TreeWalker::Next(nsIContent* aStopNode)
|
||||
{
|
||||
if (mStateStack.IsEmpty()) {
|
||||
if (mPhase == eAtEnd) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mPhase == eAtDOM || mPhase == eAtARIAOwns) {
|
||||
mPhase = eAtARIAOwns;
|
||||
Accessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx);
|
||||
if (child) {
|
||||
mARIAOwnsIdx++;
|
||||
return child;
|
||||
}
|
||||
mPhase = eAtEnd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mAnchorNode) {
|
||||
mPhase = eAtEnd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPhase = eAtDOM;
|
||||
PushState(mAnchorNode, true);
|
||||
}
|
||||
|
||||
dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
|
||||
while (top) {
|
||||
if (aStopNode && top->Get() == aStopNode) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (nsIContent* childNode = top->GetNextChild()) {
|
||||
bool skipSubtree = false;
|
||||
Accessible* child = AccessibleFor(childNode, mFlags, &skipSubtree);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// Walk down the subtree if allowed, otherwise check if we have reached
|
||||
// a stop node.
|
||||
if (!skipSubtree && childNode->IsElement()) {
|
||||
top = PushState(childNode, true);
|
||||
}
|
||||
else if (childNode == aStopNode) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
top = PopState();
|
||||
}
|
||||
|
||||
// If we traversed the whole subtree of the anchor node. Move to next node
|
||||
// relative anchor node within the context subtree if possible.
|
||||
if (mFlags != eWalkContextTree)
|
||||
return nullptr;
|
||||
// relative anchor node within the context subtree if asked.
|
||||
if (mFlags != eWalkContextTree) {
|
||||
// eWalkCache flag presence indicates that the search is scoped to the
|
||||
// anchor (no ARIA owns stuff).
|
||||
if (mFlags & eWalkCache) {
|
||||
mPhase = eAtEnd;
|
||||
return nullptr;
|
||||
}
|
||||
return Next(aStopNode);
|
||||
}
|
||||
|
||||
nsINode* contextNode = mContext->GetNode();
|
||||
while (mAnchorNode != contextNode) {
|
||||
@@ -79,10 +181,10 @@ TreeWalker::Next()
|
||||
return nullptr;
|
||||
|
||||
nsIContent* parent = parentNode->AsElement();
|
||||
top = PushState(parent);
|
||||
if (top->mDOMIter.Seek(mAnchorNode)) {
|
||||
top = PushState(parent, true);
|
||||
if (top->Seek(mAnchorNode)) {
|
||||
mAnchorNode = parent;
|
||||
return Next();
|
||||
return Next(aStopNode);
|
||||
}
|
||||
|
||||
// XXX We really should never get here, it means we're trying to find an
|
||||
@@ -92,58 +194,110 @@ TreeWalker::Next()
|
||||
mAnchorNode = parent;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return Next(aStopNode);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible,
|
||||
bool* aSkipSubtree)
|
||||
Accessible*
|
||||
TreeWalker::Prev()
|
||||
{
|
||||
nsIContent* childEl = aIter->mDOMIter.GetNextChild();
|
||||
if (!aAccesible)
|
||||
return childEl;
|
||||
|
||||
*aAccesible = nullptr;
|
||||
*aSkipSubtree = false;
|
||||
|
||||
if (childEl) {
|
||||
Accessible* accessible = nullptr;
|
||||
if (mFlags & eWalkCache) {
|
||||
accessible = mDoc->GetAccessible(childEl);
|
||||
}
|
||||
else if (mContext->IsAcceptableChild(childEl)) {
|
||||
accessible = GetAccService()->
|
||||
GetOrCreateAccessible(childEl, mContext, aSkipSubtree);
|
||||
if (mStateStack.IsEmpty()) {
|
||||
if (mPhase == eAtStart || mPhase == eAtDOM) {
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ignore the accessible and its subtree if it was repositioned by means of
|
||||
// aria-owns.
|
||||
if (accessible) {
|
||||
if (accessible->IsRelocated()) {
|
||||
*aSkipSubtree = true;
|
||||
} else {
|
||||
*aAccesible = accessible;
|
||||
if (mPhase == eAtEnd) {
|
||||
mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext);
|
||||
mPhase = eAtARIAOwns;
|
||||
}
|
||||
|
||||
if (mPhase == eAtARIAOwns) {
|
||||
if (mARIAOwnsIdx > 0) {
|
||||
return mDoc->ARIAOwnedAt(mContext, --mARIAOwnsIdx);
|
||||
}
|
||||
|
||||
if (!mAnchorNode) {
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPhase = eAtDOM;
|
||||
PushState(mAnchorNode, false);
|
||||
}
|
||||
}
|
||||
|
||||
dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
|
||||
while (top) {
|
||||
while (nsIContent* childNode = top->GetPreviousChild()) {
|
||||
// No accessible creation on the way back.
|
||||
bool skipSubtree = false;
|
||||
Accessible* child = AccessibleFor(childNode, eWalkCache, &skipSubtree);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// Walk down into subtree to find accessibles.
|
||||
if (!skipSubtree && childNode->IsElement()) {
|
||||
top = PushState(childNode, false);
|
||||
}
|
||||
}
|
||||
return childEl;
|
||||
top = PopState();
|
||||
}
|
||||
|
||||
// At last iterate over ARIA owned children.
|
||||
Accessible* parent = mDoc->GetAccessible(aIter->mDOMIter.Parent());
|
||||
if (parent) {
|
||||
Accessible* child = mDoc->ARIAOwnedAt(parent, aIter->mARIAOwnsIdx++);
|
||||
if (child) {
|
||||
*aAccesible = child;
|
||||
return child->GetContent();
|
||||
}
|
||||
// Move to a previous node relative the anchor node within the context
|
||||
// subtree if asked.
|
||||
if (mFlags != eWalkContextTree) {
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsINode* contextNode = mContext->GetNode();
|
||||
while (mAnchorNode != contextNode) {
|
||||
nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
|
||||
if (!parentNode || !parentNode->IsElement()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* parent = parentNode->AsElement();
|
||||
top = PushState(parent, true);
|
||||
if (top->Seek(mAnchorNode)) {
|
||||
mAnchorNode = parent;
|
||||
return Prev();
|
||||
}
|
||||
|
||||
mAnchorNode = parent;
|
||||
}
|
||||
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TreeWalker::ChildrenIterator*
|
||||
Accessible*
|
||||
TreeWalker::AccessibleFor(nsIContent* aNode, uint32_t aFlags, bool* aSkipSubtree)
|
||||
{
|
||||
Accessible* child = nullptr;
|
||||
if (aFlags & eWalkCache) {
|
||||
child = mDoc->GetAccessible(aNode);
|
||||
}
|
||||
else if (mContext->IsAcceptableChild(aNode)) {
|
||||
child = GetAccService()->
|
||||
GetOrCreateAccessible(aNode, mContext, aSkipSubtree);
|
||||
}
|
||||
|
||||
// Ignore the accessible and its subtree if it was repositioned by means
|
||||
// of aria-owns.
|
||||
if (child && child->IsRelocated()) {
|
||||
*aSkipSubtree = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
dom::AllChildrenIterator*
|
||||
TreeWalker::PopState()
|
||||
{
|
||||
size_t length = mStateStack.Length();
|
||||
mStateStack.RemoveElementAt(length - 1);
|
||||
return mStateStack.IsEmpty() ? nullptr : &mStateStack[mStateStack.Length() - 1];
|
||||
return mStateStack.IsEmpty() ? nullptr : &mStateStack.LastElement();
|
||||
}
|
||||
|
||||
@@ -33,63 +33,94 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Used to navigate and create if needed the accessible children.
|
||||
*/
|
||||
explicit TreeWalker(Accessible* aContext);
|
||||
|
||||
/**
|
||||
* Used to navigate the accessible children relative to the anchor.
|
||||
*
|
||||
* @param aContext [in] container accessible for the given node, used to
|
||||
* define accessible context
|
||||
* @param aNode [in] the node the search will be prepared relative to
|
||||
* @param aAnchorNode [in] the node the search will be prepared relative to
|
||||
* @param aFlags [in] flags (see enum above)
|
||||
*/
|
||||
TreeWalker(Accessible* aContext, nsIContent* aNode, uint32_t aFlags = 0);
|
||||
TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags = eWalkCache);
|
||||
|
||||
~TreeWalker();
|
||||
|
||||
/**
|
||||
* Return the next accessible.
|
||||
* Clears the tree walker state and resets it to the given child within
|
||||
* the anchor.
|
||||
*/
|
||||
bool Seek(nsIContent* aChildNode);
|
||||
|
||||
/**
|
||||
* Return the next/prev accessible.
|
||||
*
|
||||
* @note Returned accessible is bound to the document, if the accessible is
|
||||
* rejected during tree creation then the caller should be unbind it
|
||||
* from the document.
|
||||
*/
|
||||
Accessible* Next();
|
||||
Accessible* Next(nsIContent* aStopNode = nullptr);
|
||||
Accessible* Prev();
|
||||
|
||||
Accessible* Context() const { return mContext; }
|
||||
DocAccessible* Document() const { return mDoc; }
|
||||
|
||||
private:
|
||||
TreeWalker();
|
||||
TreeWalker(const TreeWalker&);
|
||||
TreeWalker& operator =(const TreeWalker&);
|
||||
|
||||
struct ChildrenIterator {
|
||||
ChildrenIterator(nsIContent* aNode, uint32_t aFilter) :
|
||||
mDOMIter(aNode, aFilter), mARIAOwnsIdx(0) { }
|
||||
|
||||
dom::AllChildrenIterator mDOMIter;
|
||||
uint32_t mARIAOwnsIdx;
|
||||
};
|
||||
|
||||
nsIContent* Next(ChildrenIterator* aIter, Accessible** aAccessible = nullptr,
|
||||
bool* aSkipSubtree = nullptr);
|
||||
/**
|
||||
* Return an accessible for the given node if any.
|
||||
*/
|
||||
Accessible* AccessibleFor(nsIContent* aNode, uint32_t aFlags,
|
||||
bool* aSkipSubtree);
|
||||
|
||||
/**
|
||||
* Create new state for the given node and push it on top of stack.
|
||||
* Create new state for the given node and push it on top of stack / at bottom
|
||||
* of stack.
|
||||
*
|
||||
* @note State stack is used to navigate up/down the DOM subtree during
|
||||
* accessible children search.
|
||||
*/
|
||||
ChildrenIterator* PushState(nsIContent* aContent)
|
||||
dom::AllChildrenIterator* PushState(nsIContent* aContent,
|
||||
bool aStartAtBeginning)
|
||||
{
|
||||
return mStateStack.AppendElement(ChildrenIterator(aContent, mChildFilter));
|
||||
return mStateStack.AppendElement(
|
||||
dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning));
|
||||
}
|
||||
dom::AllChildrenIterator* PrependState(nsIContent* aContent,
|
||||
bool aStartAtBeginning)
|
||||
{
|
||||
return mStateStack.InsertElementAt(0,
|
||||
dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop state from stack.
|
||||
*/
|
||||
ChildrenIterator* PopState();
|
||||
dom::AllChildrenIterator* PopState();
|
||||
|
||||
DocAccessible* mDoc;
|
||||
Accessible* mContext;
|
||||
nsIContent* mAnchorNode;
|
||||
AutoTArray<ChildrenIterator, 20> mStateStack;
|
||||
|
||||
AutoTArray<dom::AllChildrenIterator, 20> mStateStack;
|
||||
uint32_t mARIAOwnsIdx;
|
||||
|
||||
int32_t mChildFilter;
|
||||
uint32_t mFlags;
|
||||
|
||||
enum Phase {
|
||||
eAtStart,
|
||||
eAtDOM,
|
||||
eAtARIAOwns,
|
||||
eAtEnd
|
||||
};
|
||||
Phase mPhase;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
||||
@@ -1983,6 +1983,10 @@ Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
|
||||
|
||||
if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent))
|
||||
SetARIAHidden(true);
|
||||
|
||||
mContextFlags |=
|
||||
static_cast<uint32_t>((mParent->IsAlert() ||
|
||||
mParent->IsInsideAlert())) & eInsideAlert;
|
||||
}
|
||||
|
||||
// Accessible protected
|
||||
@@ -2000,7 +2004,7 @@ Accessible::UnbindFromParent()
|
||||
|
||||
delete mBits.groupInfo;
|
||||
mBits.groupInfo = nullptr;
|
||||
mContextFlags &= ~eHasNameDependentParent;
|
||||
mContextFlags &= ~eHasNameDependentParent & ~eInsideAlert;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -2542,10 +2546,9 @@ Accessible::LastRelease()
|
||||
void
|
||||
Accessible::CacheChildren()
|
||||
{
|
||||
DocAccessible* doc = Document();
|
||||
NS_ENSURE_TRUE_VOID(doc);
|
||||
NS_ENSURE_TRUE_VOID(Document());
|
||||
|
||||
TreeWalker walker(this, mContent);
|
||||
TreeWalker walker(this);
|
||||
|
||||
Accessible* child = nullptr;
|
||||
while ((child = walker.Next()) && AppendChild(child));
|
||||
|
||||
@@ -568,6 +568,8 @@ public:
|
||||
return mContent->IsAnyOfHTMLElements(nsGkAtoms::abbr, nsGkAtoms::acronym);
|
||||
}
|
||||
|
||||
bool IsAlert() const { return HasGenericType(eAlert); }
|
||||
|
||||
bool IsApplication() const { return mType == eApplicationType; }
|
||||
ApplicationAccessible* AsApplication();
|
||||
|
||||
@@ -946,6 +948,11 @@ public:
|
||||
bool IsARIAHidden() const { return mContextFlags & eARIAHidden; }
|
||||
void SetARIAHidden(bool aIsDefined);
|
||||
|
||||
/**
|
||||
* Return true if the element is inside an alert.
|
||||
*/
|
||||
bool IsInsideAlert() const { return mContextFlags & eInsideAlert; }
|
||||
|
||||
protected:
|
||||
virtual ~Accessible();
|
||||
|
||||
@@ -1034,8 +1041,9 @@ protected:
|
||||
enum ContextFlags {
|
||||
eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible.
|
||||
eARIAHidden = 1 << 1,
|
||||
eInsideAlert = 1 << 2,
|
||||
|
||||
eLastContextFlag = eARIAHidden
|
||||
eLastContextFlag = eInsideAlert
|
||||
};
|
||||
|
||||
protected:
|
||||
@@ -1141,9 +1149,9 @@ protected:
|
||||
|
||||
static const uint8_t kChildrenFlagsBits = 2;
|
||||
static const uint8_t kStateFlagsBits = 11;
|
||||
static const uint8_t kContextFlagsBits = 2;
|
||||
static const uint8_t kContextFlagsBits = 3;
|
||||
static const uint8_t kTypeBits = 6;
|
||||
static const uint8_t kGenericTypesBits = 14;
|
||||
static const uint8_t kGenericTypesBits = 15;
|
||||
|
||||
/**
|
||||
* Keep in sync with ChildrenFlags, StateFlags, ContextFlags, and AccTypes.
|
||||
|
||||
@@ -1426,36 +1426,6 @@ if (!aNode->IsContent() || !aNode->AsContent()->IsHTMLElement(nsGkAtoms::area))
|
||||
return GetAccessible(aNode);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Accessible protected
|
||||
|
||||
void
|
||||
DocAccessible::CacheChildren()
|
||||
{
|
||||
// Search for accessible children starting from the document element since
|
||||
// some web pages tend to insert elements under it rather than document body.
|
||||
dom::Element* rootElm = mDocumentNode->GetRootElement();
|
||||
if (!rootElm)
|
||||
return;
|
||||
|
||||
// Ignore last HTML:br, copied from HyperTextAccessible.
|
||||
TreeWalker walker(this, rootElm);
|
||||
Accessible* lastChild = nullptr;
|
||||
while (Accessible* child = walker.Next()) {
|
||||
if (lastChild)
|
||||
AppendChild(lastChild);
|
||||
|
||||
lastChild = child;
|
||||
}
|
||||
|
||||
if (lastChild) {
|
||||
if (lastChild->IsHTMLBr())
|
||||
Document()->UnbindFromDocument(lastChild);
|
||||
else
|
||||
AppendChild(lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected members
|
||||
|
||||
@@ -1791,22 +1761,16 @@ DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer)
|
||||
|
||||
// Check to see if change occurred inside an alert, and fire an EVENT_ALERT
|
||||
// if it did.
|
||||
if (!(updateFlags & eAlertAccessible)) {
|
||||
// XXX: tree traversal is perf issue, accessible should know if they are
|
||||
// children of alert accessible to avoid this.
|
||||
if (!(updateFlags & eAlertAccessible) &&
|
||||
(aContainer->IsAlert() || aContainer->IsInsideAlert())) {
|
||||
Accessible* ancestor = aContainer;
|
||||
while (ancestor) {
|
||||
if (ancestor->ARIARole() == roles::ALERT) {
|
||||
do {
|
||||
if (ancestor->IsAlert()) {
|
||||
FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't climb above this document.
|
||||
if (ancestor == this)
|
||||
break;
|
||||
|
||||
ancestor = ancestor->Parent();
|
||||
}
|
||||
while ((ancestor = ancestor->Parent()));
|
||||
}
|
||||
|
||||
MaybeNotifyOfValueChange(aContainer);
|
||||
@@ -1839,34 +1803,9 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod
|
||||
if (child) {
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
} else {
|
||||
// aChildNode may not coorespond to a particular accessible, to handle
|
||||
// this we go through all the children of aContainer. Then if a child
|
||||
// has aChildNode as an ancestor, or does not have the node for
|
||||
// aContainer as an ancestor remove that child of aContainer. Note that
|
||||
// when we are called aChildNode may already have been removed from the DOM
|
||||
// so we can't expect it to have a parent or what was it's parent to have
|
||||
// it as a child.
|
||||
nsINode* containerNode = aContainer->GetNode();
|
||||
for (uint32_t idx = 0; idx < aContainer->ContentChildCount();) {
|
||||
Accessible* child = aContainer->ContentChildAt(idx);
|
||||
|
||||
// If accessible doesn't have its own content then we assume parent
|
||||
// will handle its update. If child is DocAccessible then we don't
|
||||
// handle updating it here either.
|
||||
if (!child->HasOwnContent() || child->IsDoc()) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
nsINode* childNode = child->GetContent();
|
||||
while (childNode != aChildNode && childNode != containerNode &&
|
||||
(childNode = childNode->GetParentNode()));
|
||||
|
||||
if (childNode != containerNode) {
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
|
||||
while (Accessible* child = walker.Next()) {
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2062,6 +2001,7 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
|
||||
MoveChild(child, insertIdx);
|
||||
children->InsertElementAt(arrayIdx, child);
|
||||
arrayIdx++;
|
||||
insertIdx = child->IndexInParent() + 1;
|
||||
|
||||
} else if (SeizeChild(aOwner, child, insertIdx)) {
|
||||
children->InsertElementAt(arrayIdx, child);
|
||||
@@ -2088,6 +2028,12 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
|
||||
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("aria owns seize child", 0,
|
||||
"old parent", oldParent, "new parent", aNewParent,
|
||||
"child", aChild, nullptr);
|
||||
#endif
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
@@ -2103,6 +2049,11 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("aria owns seize child: new parent tree after",
|
||||
logging::eVerbose, aNewParent);
|
||||
#endif
|
||||
|
||||
if (!isReinserted) {
|
||||
AutoTreeMutation mut(oldParent);
|
||||
oldParent->InsertChildAt(oldIdxInParent, aChild);
|
||||
@@ -2141,10 +2092,20 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("aria owns move child", 0,
|
||||
"parent", parent, "child", aChild, nullptr);
|
||||
#endif
|
||||
|
||||
AutoTreeMutation mut(parent);
|
||||
parent->MoveChild(aIdxInParent, aChild);
|
||||
aChild->SetRelocated(true);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("aria owns move child: parent tree after",
|
||||
logging::eVerbose, parent);
|
||||
#endif
|
||||
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
|
||||
@@ -291,6 +291,11 @@ public:
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t ARIAOwnedCount(Accessible* aParent) const
|
||||
{
|
||||
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(aParent);
|
||||
return children ? children->Length() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given ID is referred by relation attribute.
|
||||
@@ -359,9 +364,6 @@ protected:
|
||||
|
||||
void LastRelease();
|
||||
|
||||
// Accessible
|
||||
virtual void CacheChildren() override;
|
||||
|
||||
// DocAccessible
|
||||
virtual nsresult AddEventListeners();
|
||||
virtual nsresult RemoveEventListeners();
|
||||
|
||||
@@ -1936,31 +1936,6 @@ HyperTextAccessible::RelationByType(RelationType aType)
|
||||
return rel;
|
||||
}
|
||||
|
||||
void
|
||||
HyperTextAccessible::CacheChildren()
|
||||
{
|
||||
// Trailing HTML br element don't play any difference. We don't need to expose
|
||||
// it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
|
||||
// for details).
|
||||
|
||||
TreeWalker walker(this, mContent);
|
||||
Accessible* child = nullptr;
|
||||
Accessible* lastChild = nullptr;
|
||||
while ((child = walker.Next())) {
|
||||
if (lastChild)
|
||||
AppendChild(lastChild);
|
||||
|
||||
lastChild = child;
|
||||
}
|
||||
|
||||
if (lastChild) {
|
||||
if (lastChild->IsHTMLBr())
|
||||
Document()->UnbindFromDocument(lastChild);
|
||||
else
|
||||
AppendChild(lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HyperTextAccessible public static
|
||||
|
||||
|
||||
@@ -434,7 +434,6 @@ protected:
|
||||
|
||||
// Accessible
|
||||
virtual ENameValueFlag NativeName(nsString& aName) override;
|
||||
virtual void CacheChildren() override;
|
||||
|
||||
// HyperTextAccessible
|
||||
|
||||
|
||||
@@ -126,9 +126,23 @@ function dumpTree(aId, aMsg)
|
||||
}
|
||||
}
|
||||
|
||||
function dumpDOMTreeIntl(node, indent)
|
||||
{
|
||||
dump(indent + prettyName(node) + "\n");
|
||||
|
||||
var children = node.childNodes;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children.item(i);
|
||||
dumpDOMTreeIntl(child, indent + " ");
|
||||
}
|
||||
}
|
||||
|
||||
dump(aMsg + "\n");
|
||||
var root = getAccessible(aId);
|
||||
dumpTreeIntl(root, " ");
|
||||
|
||||
dump("DOM tree:\n");
|
||||
dumpDOMTreeIntl(getNode(aId), " ");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,6 +62,7 @@ function editableTextTest(aID)
|
||||
|
||||
function setTextContentsInvoke()
|
||||
{
|
||||
dump(`\nsetTextContents '${aValue}'\n`);
|
||||
var acc = getAccessible(aID, nsIAccessibleEditableText);
|
||||
acc.setTextContents(aValue);
|
||||
}
|
||||
@@ -84,6 +85,7 @@ function editableTextTest(aID)
|
||||
|
||||
function insertTextInvoke()
|
||||
{
|
||||
dump(`\ninsertText '${aStr}' at ${aPos} pos\n`);
|
||||
var acc = getAccessible(aID, nsIAccessibleEditableText);
|
||||
acc.insertText(aStr, aPos);
|
||||
}
|
||||
|
||||
@@ -153,6 +153,7 @@
|
||||
var gQueue = null;
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debuging
|
||||
//enableLogging("tree,verbose");
|
||||
|
||||
function doTests()
|
||||
{
|
||||
|
||||
@@ -441,12 +441,81 @@
|
||||
}
|
||||
}
|
||||
|
||||
function rearrangeARIAOwns(aContainer, aAttr, aIdList, aRoleList)
|
||||
{
|
||||
this.eventSeq = [];
|
||||
for (var id of aIdList) {
|
||||
this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id)));
|
||||
this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id)));
|
||||
}
|
||||
this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer)));
|
||||
|
||||
this.invoke = function rearrangeARIAOwns_invoke()
|
||||
{
|
||||
getNode(aContainer).setAttribute("aria-owns", aAttr);
|
||||
}
|
||||
|
||||
this.finalCheck = function rearrangeARIAOwns_finalCheck()
|
||||
{
|
||||
var tree = { SECTION: [ ] };
|
||||
for (var role of aRoleList) {
|
||||
var ch = {};
|
||||
ch[role] = [];
|
||||
tree["SECTION"].push(ch);
|
||||
}
|
||||
testAccessibleTree(aContainer, tree);
|
||||
}
|
||||
|
||||
this.getID = function rearrangeARIAOwns_getID()
|
||||
{
|
||||
return `Rearrange @aria-owns attribute to '${aAttr}'`;
|
||||
}
|
||||
}
|
||||
|
||||
function removeNotARIAOwnedEl(aContainer, aChild)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, aContainer)
|
||||
];
|
||||
|
||||
this.invoke = function removeNotARIAOwnedEl_invoke()
|
||||
{
|
||||
dumpTree(aContainer, "before");
|
||||
var tree = {
|
||||
SECTION: [
|
||||
{ TEXT_LEAF: [ ] },
|
||||
{ GROUPING: [ ] }
|
||||
]
|
||||
};
|
||||
testAccessibleTree(aContainer, tree);
|
||||
|
||||
getNode(aContainer).removeChild(getNode(aChild));
|
||||
}
|
||||
|
||||
this.finalCheck = function removeNotARIAOwnedEl_finalCheck()
|
||||
{
|
||||
dumpTree(aContainer, "after");
|
||||
var tree = {
|
||||
SECTION: [
|
||||
{ GROUPING: [ ] }
|
||||
]
|
||||
};
|
||||
testAccessibleTree(aContainer, tree);
|
||||
}
|
||||
|
||||
this.getID = function removeNotARIAOwnedEl_getID()
|
||||
{
|
||||
return `remove not ARIA owned child`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Test
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//gA11yEventDumpToConsole = true;
|
||||
//enableLogging("tree"); // debug stuff
|
||||
//enableLogging("tree,verbose"); // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
@@ -474,6 +543,18 @@
|
||||
// test4
|
||||
gQueue.push(new showHiddenElement());
|
||||
|
||||
// test5
|
||||
gQueue.push(new rearrangeARIAOwns(
|
||||
"t5_container", "t5_checkbox t5_radio t5_button",
|
||||
[ "t5_checkbox", "t5_radio", "t5_button" ],
|
||||
[ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ]));
|
||||
gQueue.push(new rearrangeARIAOwns(
|
||||
"t5_container", "t5_radio t5_button t5_checkbox",
|
||||
[ "t5_radio", "t5_button" ],
|
||||
[ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ]));
|
||||
|
||||
gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span"));
|
||||
|
||||
gQueue.invoke(); // SimpleTest.finish() will be called in the end
|
||||
}
|
||||
|
||||
@@ -515,6 +596,17 @@
|
||||
<div id="t4_child1" style="display:none" role="checkbox"></div>
|
||||
<div id="t4_child2" role="radio"></div>
|
||||
</div>
|
||||
|
||||
<div id="t5_container">
|
||||
<div role="button" id="t5_button"></div>
|
||||
<div role="checkbox" id="t5_checkbox"></div>
|
||||
<div role="radio" id="t5_radio"></div>
|
||||
</div>
|
||||
|
||||
<div id="t6_container" aria-owns="t6_fake">
|
||||
<span id="t6_span">hey</span>
|
||||
</div>
|
||||
<div id="t6_fake" role="group"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -19,6 +19,7 @@ XULAlertAccessible::
|
||||
XULAlertAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
AccessibleWrap(aContent, aDoc)
|
||||
{
|
||||
mGenericTypes |= eAlert;
|
||||
}
|
||||
|
||||
XULAlertAccessible::~XULAlertAccessible()
|
||||
|
||||
@@ -29,17 +29,12 @@ function serializeServiceWorkerInfo(aServiceWorkerInfo) {
|
||||
|
||||
let result = {};
|
||||
|
||||
Object.keys(aServiceWorkerInfo).forEach(property => {
|
||||
if (typeof aServiceWorkerInfo[property] == "function") {
|
||||
return;
|
||||
}
|
||||
if (property === "principal") {
|
||||
result.principal = {
|
||||
origin: aServiceWorkerInfo.principal.origin,
|
||||
originAttributes: aServiceWorkerInfo.principal.originAttributes
|
||||
};
|
||||
return;
|
||||
}
|
||||
result.principal = {
|
||||
origin: aServiceWorkerInfo.principal.originNoSuffix,
|
||||
originAttributes: aServiceWorkerInfo.principal.originAttributes
|
||||
};
|
||||
|
||||
["scope", "scriptSpec"].forEach(property => {
|
||||
result[property] = aServiceWorkerInfo[property];
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Cu.import("resource://gre/modules/PermissionsTable.jsm");
|
||||
var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
|
||||
var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
|
||||
|
||||
let permissionSpecificChecker = {};
|
||||
var permissionSpecificChecker = {};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"TelephonyService",
|
||||
@@ -413,7 +413,10 @@ ContentPermissionPrompt.prototype = {
|
||||
type: type,
|
||||
permissions: permissions,
|
||||
id: requestId,
|
||||
origin: principal.origin,
|
||||
// This system app uses the origin from permission events to
|
||||
// compare against the mozApp.origin of app windows, so we
|
||||
// are not concerned with origin suffixes here (appId, etc).
|
||||
origin: principal.originNoSuffix,
|
||||
isApp: isApp,
|
||||
remember: remember,
|
||||
isGranted: isGranted,
|
||||
|
||||
@@ -23,7 +23,7 @@ MailtoProtocolHandler.prototype = {
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
|
||||
Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
|
||||
allowPort: function() false,
|
||||
allowPort: () => false,
|
||||
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
|
||||
@@ -62,13 +62,13 @@ function hookScreen(window) {
|
||||
};
|
||||
|
||||
Object.defineProperty(screen, 'width', {
|
||||
get: function () GlobalSimulatorScreen.width
|
||||
get: () => GlobalSimulatorScreen.width
|
||||
});
|
||||
Object.defineProperty(screen, 'height', {
|
||||
get: function () GlobalSimulatorScreen.height
|
||||
get: () => GlobalSimulatorScreen.height
|
||||
});
|
||||
Object.defineProperty(screen, 'mozOrientation', {
|
||||
get: function () GlobalSimulatorScreen.mozOrientation
|
||||
get: () => GlobalSimulatorScreen.mozOrientation
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ SmsProtocolHandler.prototype = {
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
|
||||
Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
|
||||
allowPort: function() false,
|
||||
allowPort: () => false,
|
||||
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
|
||||
@@ -31,7 +31,7 @@ TelProtocolHandler.prototype = {
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
|
||||
Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
|
||||
allowPort: function() false,
|
||||
allowPort: () => false,
|
||||
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
|
||||
@@ -21,10 +21,10 @@ const { EventEmitter } = Cu.import("resource://gre/modules/devtools/shared/event
|
||||
// have trailing newlines. And note that registerLogHandler actually registers
|
||||
// an error handler, despite its name.
|
||||
Subprocess.registerLogHandler(
|
||||
function(s) console.error("subprocess: " + s.trim())
|
||||
s => console.error("subprocess: " + s.trim())
|
||||
);
|
||||
Subprocess.registerDebugHandler(
|
||||
function(s) console.debug("subprocess: " + s.trim())
|
||||
s => console.debug("subprocess: " + s.trim())
|
||||
);
|
||||
|
||||
function SimulatorProcess(options) {
|
||||
@@ -38,7 +38,9 @@ function SimulatorProcess(options) {
|
||||
SimulatorProcess.prototype = {
|
||||
|
||||
// check if b2g is running
|
||||
get isRunning() !!this.process,
|
||||
get isRunning() {
|
||||
return !!this.process;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the process and connect the debugger client.
|
||||
|
||||
@@ -5158,6 +5158,14 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
|
||||
menuItem.addEventListener("command", onViewToolbarCommand, false);
|
||||
}
|
||||
|
||||
|
||||
let moveToPanel = popup.querySelector(".customize-context-moveToPanel");
|
||||
let removeFromToolbar = popup.querySelector(".customize-context-removeFromToolbar");
|
||||
// View -> Toolbars menu doesn't have the moveToPanel or removeFromToolbar items.
|
||||
if (!moveToPanel || !removeFromToolbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The explicitOriginalTarget can be a nested child element of a toolbaritem.
|
||||
let toolbarItem = aEvent.explicitOriginalTarget;
|
||||
|
||||
|
||||
@@ -552,12 +552,18 @@ nsContextMenu.prototype = {
|
||||
LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
|
||||
},
|
||||
|
||||
inspectNode: function CM_inspectNode() {
|
||||
inspectNode: function() {
|
||||
let {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
|
||||
let tt = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
||||
return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
|
||||
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
return gDevTools.showToolbox(target, "inspector").then(toolbox => {
|
||||
let inspector = toolbox.getCurrentPanel();
|
||||
|
||||
// new-node-front tells us when the node has been selected, whether the
|
||||
// browser is remote or not.
|
||||
let onNewNode = inspector.selection.once("new-node-front");
|
||||
|
||||
if (this.isRemote) {
|
||||
this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target});
|
||||
inspector.walker.findInspectingNode().then(nodeFront => {
|
||||
@@ -566,7 +572,13 @@ nsContextMenu.prototype = {
|
||||
} else {
|
||||
inspector.selection.setNode(this.target, "browser-context-menu");
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return onNewNode.then(() => {
|
||||
// Now that the node has been selected, wait until the inspector is
|
||||
// fully updated.
|
||||
return inspector.once("inspector-updated");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Set various context menu attributes based on the state of the world.
|
||||
|
||||
@@ -106,6 +106,24 @@ AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray<nsIFile> &array)
|
||||
|
||||
localePlugins->AppendNative(NS_LITERAL_CSTRING("locale"));
|
||||
|
||||
nsCString defLocale;
|
||||
rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale",
|
||||
getter_Copies(defLocale));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
||||
nsCOMPtr<nsIFile> defLocalePlugins;
|
||||
rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
||||
defLocalePlugins->AppendNative(defLocale);
|
||||
rv = defLocalePlugins->Exists(&exists);
|
||||
if (NS_SUCCEEDED(rv) && exists)
|
||||
array.AppendObject(defLocalePlugins);
|
||||
return; // all done
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't have a defaultLocale, use the user agent locale
|
||||
nsCString locale;
|
||||
nsCOMPtr<nsIPrefLocalizedString> prefString;
|
||||
rv = prefs->GetComplexValue("general.useragent.locale",
|
||||
@@ -133,23 +151,6 @@ AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray<nsIFile> &array)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't append the locale dir - try the default one
|
||||
nsCString defLocale;
|
||||
rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale",
|
||||
getter_Copies(defLocale));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
||||
nsCOMPtr<nsIFile> defLocalePlugins;
|
||||
rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
||||
defLocalePlugins->AppendNative(defLocale);
|
||||
rv = defLocalePlugins->Exists(&exists);
|
||||
if (NS_SUCCEEDED(rv) && exists)
|
||||
array.AppendObject(defLocalePlugins);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC =
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
@@ -70,6 +71,12 @@ DistributionCustomizer.prototype = {
|
||||
return this._locale;
|
||||
},
|
||||
|
||||
get _language() {
|
||||
let language = this._locale.split("-")[0];
|
||||
this.__defineGetter__("_language", () => language);
|
||||
return this._language;
|
||||
},
|
||||
|
||||
get _prefSvc() {
|
||||
let svc = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService);
|
||||
@@ -112,6 +119,8 @@ DistributionCustomizer.prototype = {
|
||||
|
||||
if (keys.indexOf(key + "." + this._locale) >= 0) {
|
||||
key += "." + this._locale;
|
||||
} else if (keys.indexOf(key + "." + this._language) >= 0) {
|
||||
key += "." + this._language;
|
||||
}
|
||||
|
||||
if (!items[itemIndex])
|
||||
@@ -309,76 +318,124 @@ DistributionCustomizer.prototype = {
|
||||
if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"]))
|
||||
return this._checkCustomizationComplete();
|
||||
|
||||
let defaults = this._prefSvc.getDefaultBranch(null);
|
||||
let defaults = new Preferences({defaultBranch: true});
|
||||
|
||||
// Global really contains info we set as prefs. They're only
|
||||
// separate because they are "special" (read: required)
|
||||
|
||||
defaults.setCharPref("distribution.id", this._ini.getString("Global", "id"));
|
||||
defaults.setCharPref("distribution.version",
|
||||
this._ini.getString("Global", "version"));
|
||||
defaults.set("distribution.id", this._ini.getString("Global", "id"));
|
||||
defaults.set("distribution.version", this._ini.getString("Global", "version"));
|
||||
|
||||
let partnerAbout = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
let partnerAbout;
|
||||
try {
|
||||
if (globalPrefs["about." + this._locale]) {
|
||||
partnerAbout.data = this._ini.getString("Global", "about." + this._locale);
|
||||
partnerAbout = this._ini.getString("Global", "about." + this._locale);
|
||||
} else if (globalPrefs["about." + this._language]) {
|
||||
partnerAbout = this._ini.getString("Global", "about." + this._language);
|
||||
} else {
|
||||
partnerAbout.data = this._ini.getString("Global", "about");
|
||||
partnerAbout = this._ini.getString("Global", "about");
|
||||
}
|
||||
defaults.setComplexValue("distribution.about",
|
||||
Ci.nsISupportsString, partnerAbout);
|
||||
defaults.set("distribution.about", partnerAbout);
|
||||
} catch (e) {
|
||||
/* ignore bad prefs due to bug 895473 and move on */
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
var usedPreferences = [];
|
||||
|
||||
if (sections["Preferences-" + this._locale]) {
|
||||
for (let key of enumerate(this._ini.getKeys("Preferences-" + this._locale))) {
|
||||
try {
|
||||
let value = this._ini.getString("Preferences-" + this._locale, key);
|
||||
if (value) {
|
||||
Preferences.set(key, parseValue(value));
|
||||
}
|
||||
usedPreferences.push(key);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (sections["Preferences-" + this._language]) {
|
||||
for (let key of enumerate(this._ini.getKeys("Preferences-" + this._language))) {
|
||||
if (usedPreferences.indexOf(key) > -1) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
let value = this._ini.getString("Preferences-" + this._language, key);
|
||||
if (value) {
|
||||
Preferences.set(key, parseValue(value));
|
||||
}
|
||||
usedPreferences.push(key);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (sections["Preferences"]) {
|
||||
for (let key of enumerate(this._ini.getKeys("Preferences"))) {
|
||||
if (usedPreferences.indexOf(key) > -1) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
let value = eval(this._ini.getString("Preferences", key));
|
||||
switch (typeof value) {
|
||||
case "boolean":
|
||||
defaults.setBoolPref(key, value);
|
||||
break;
|
||||
case "number":
|
||||
defaults.setIntPref(key, value);
|
||||
break;
|
||||
case "string":
|
||||
defaults.setCharPref(key, value);
|
||||
break;
|
||||
case "undefined":
|
||||
defaults.setCharPref(key, value);
|
||||
break;
|
||||
let value = this._ini.getString("Preferences", key);
|
||||
if (value) {
|
||||
value = value.replace(/%LOCALE%/g, this._locale);
|
||||
value = value.replace(/%LANGUAGE%/g, this._language);
|
||||
Preferences.set(key, parseValue(value));
|
||||
}
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
|
||||
// We eval() the localizable prefs as well (even though they'll
|
||||
// always get set as a string) to keep the INI format consistent:
|
||||
// string prefs always need to be in quotes
|
||||
|
||||
let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"].
|
||||
createInstance(Ci.nsIPrefLocalizedString);
|
||||
|
||||
if (sections["LocalizablePreferences"]) {
|
||||
for (let key of enumerate(this._ini.getKeys("LocalizablePreferences"))) {
|
||||
try {
|
||||
let value = eval(this._ini.getString("LocalizablePreferences", key));
|
||||
value = value.replace(/%LOCALE%/g, this._locale);
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
var usedLocalizablePreferences = [];
|
||||
|
||||
if (sections["LocalizablePreferences-" + this._locale]) {
|
||||
for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
|
||||
try {
|
||||
let value = eval(this._ini.getString("LocalizablePreferences-" + this._locale, key));
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
let value = this._ini.getString("LocalizablePreferences-" + this._locale, key);
|
||||
if (value) {
|
||||
value = parseValue(value);
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
}
|
||||
usedLocalizablePreferences.push(key);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (sections["LocalizablePreferences-" + this._language]) {
|
||||
for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._language))) {
|
||||
if (usedLocalizablePreferences.indexOf(key) > -1) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
let value = this._ini.getString("LocalizablePreferences-" + this._language, key);
|
||||
if (value) {
|
||||
value = parseValue(value);
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
}
|
||||
usedLocalizablePreferences.push(key);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (sections["LocalizablePreferences"]) {
|
||||
for (let key of enumerate(this._ini.getKeys("LocalizablePreferences"))) {
|
||||
if (usedLocalizablePreferences.indexOf(key) > -1) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
let value = this._ini.getString("LocalizablePreferences", key);
|
||||
if (value) {
|
||||
value = parseValue(value);
|
||||
value = value.replace(/%LOCALE%/g, this._locale);
|
||||
value = value.replace(/%LANGUAGE%/g, this._language);
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
}
|
||||
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
@@ -397,6 +454,19 @@ DistributionCustomizer.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function parseValue(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (e) {
|
||||
// JSON.parse catches numbers and booleans.
|
||||
// Anything else, we assume is a string.
|
||||
// Remove the quotes that aren't needed anymore.
|
||||
value = value.replace(/^"/, "");
|
||||
value = value.replace(/"$/, "");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function* enumerate(UTF8Enumerator) {
|
||||
while (UTF8Enumerator.hasMore())
|
||||
yield UTF8Enumerator.getNext();
|
||||
|
||||
@@ -48,5 +48,14 @@ EXTRA_JS_MODULES += [
|
||||
'distribution.js',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'tests/browser/browser.ini'
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'tests/unit/xpcshell.ini'
|
||||
]
|
||||
|
||||
|
||||
with Files('controlcenter/**'):
|
||||
BUG_COMPONENT = ('Firefox', 'General')
|
||||
|
||||
@@ -32,13 +32,9 @@ const nsIDOMWindow = Components.interfaces.nsIDOMWindow;
|
||||
const nsIFileURL = Components.interfaces.nsIFileURL;
|
||||
const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor;
|
||||
const nsINetUtil = Components.interfaces.nsINetUtil;
|
||||
const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
|
||||
const nsIPrefLocalizedString = Components.interfaces.nsIPrefLocalizedString;
|
||||
const nsISupportsString = Components.interfaces.nsISupportsString;
|
||||
const nsIURIFixup = Components.interfaces.nsIURIFixup;
|
||||
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
|
||||
const nsIWindowMediator = Components.interfaces.nsIWindowMediator;
|
||||
const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
|
||||
const nsIWebNavigationInfo = Components.interfaces.nsIWebNavigationInfo;
|
||||
const nsICommandLineValidator = Components.interfaces.nsICommandLineValidator;
|
||||
|
||||
@@ -60,12 +56,11 @@ function shouldLoadURI(aURI) {
|
||||
|
||||
function resolveURIInternal(aCmdLine, aArgument) {
|
||||
var uri = aCmdLine.resolveURI(aArgument);
|
||||
var urifixup = Components.classes["@mozilla.org/docshell/urifixup;1"]
|
||||
.getService(nsIURIFixup);
|
||||
var uriFixup = Services.uriFixup;
|
||||
|
||||
if (!(uri instanceof nsIFileURL)) {
|
||||
return urifixup.createFixupURI(aArgument,
|
||||
urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS);
|
||||
return uriFixup.createFixupURI(aArgument,
|
||||
uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -80,7 +75,7 @@ function resolveURIInternal(aCmdLine, aArgument) {
|
||||
// doesn't exist. Try URI fixup heuristics: see bug 290782.
|
||||
|
||||
try {
|
||||
uri = urifixup.createFixupURI(aArgument, 0);
|
||||
uri = uriFixup.createFixupURI(aArgument, 0);
|
||||
}
|
||||
catch (e) {
|
||||
Components.utils.reportError(e);
|
||||
@@ -182,9 +177,6 @@ function getPostUpdateOverridePage(defaultOverridePage) {
|
||||
const NO_EXTERNAL_URIS = 1;
|
||||
|
||||
function openWindow(parent, url, target, features, args, noExternalArgs) {
|
||||
var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(nsIWindowWatcher);
|
||||
|
||||
if (noExternalArgs == NO_EXTERNAL_URIS) {
|
||||
// Just pass in the defaultArgs directly
|
||||
var argstring;
|
||||
@@ -194,7 +186,7 @@ function openWindow(parent, url, target, features, args, noExternalArgs) {
|
||||
argstring.data = args;
|
||||
}
|
||||
|
||||
return wwatch.openWindow(parent, url, target, features, argstring);
|
||||
return Services.ww.openWindow(parent, url, target, features, argstring);
|
||||
}
|
||||
|
||||
// Pass an array to avoid the browser "|"-splitting behavior.
|
||||
@@ -231,7 +223,7 @@ function openWindow(parent, url, target, features, args, noExternalArgs) {
|
||||
argArray.AppendElement(null); // postData
|
||||
argArray.AppendElement(null); // allowThirdPartyFixup
|
||||
|
||||
return wwatch.openWindow(parent, url, target, features, argArray);
|
||||
return Services.ww.openWindow(parent, url, target, features, argArray);
|
||||
}
|
||||
|
||||
function openPreferences() {
|
||||
@@ -242,8 +234,12 @@ function openPreferences() {
|
||||
if (win) {
|
||||
win.focus();
|
||||
} else {
|
||||
openWindow(null, url, "_blank", features);
|
||||
}
|
||||
return Services.ww.openWindow(null, gBrowserContentHandler.chromeURL,
|
||||
"_blank",
|
||||
"chrome,dialog=no,all" +
|
||||
gBrowserContentHandler.getFeatures(cmdLine),
|
||||
sa);
|
||||
}
|
||||
}
|
||||
|
||||
function getMostRecentWindow(aType) {
|
||||
@@ -280,14 +276,11 @@ function doSearch(searchTerm, cmdLine) {
|
||||
// XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
|
||||
// preferences, but need nsIBrowserDOMWindow extensions
|
||||
|
||||
var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(nsIWindowWatcher);
|
||||
|
||||
return wwatch.openWindow(null, gBrowserContentHandler.chromeURL,
|
||||
"_blank",
|
||||
"chrome,dialog=no,all" +
|
||||
gBrowserContentHandler.getFeatures(cmdLine),
|
||||
sa);
|
||||
return Services.ww.openWindow(null, gBrowserContentHandler.chromeURL,
|
||||
"_blank",
|
||||
"chrome,dialog=no,all" +
|
||||
gBrowserContentHandler.getFeatures(cmdLine),
|
||||
sa);
|
||||
}
|
||||
|
||||
function nsBrowserContentHandler() {
|
||||
@@ -312,9 +305,7 @@ nsBrowserContentHandler.prototype = {
|
||||
return this.mChromeURL;
|
||||
}
|
||||
|
||||
var prefb = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(nsIPrefBranch);
|
||||
this.mChromeURL = prefb.getCharPref("browser.chromeURL");
|
||||
this.mChromeURL = Services.prefs.getCharPref("browser.chromeURL");
|
||||
|
||||
return this.mChromeURL;
|
||||
},
|
||||
@@ -338,7 +329,7 @@ nsBrowserContentHandler.prototype = {
|
||||
var uriparam;
|
||||
try {
|
||||
while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
|
||||
var uri = resolveURIInternal(cmdLine, uriparam);
|
||||
let uri = resolveURIInternal(cmdLine, uriparam);
|
||||
if (!shouldLoadURI(uri))
|
||||
continue;
|
||||
openWindow(null, this.chromeURL, "_blank",
|
||||
@@ -353,7 +344,7 @@ nsBrowserContentHandler.prototype = {
|
||||
|
||||
try {
|
||||
while ((uriparam = cmdLine.handleFlagWithParam("new-tab", false))) {
|
||||
var uri = resolveURIInternal(cmdLine, uriparam);
|
||||
let uri = resolveURIInternal(cmdLine, uriparam);
|
||||
handURIToExistingBrowser(uri, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine);
|
||||
cmdLine.preventDefault = true;
|
||||
}
|
||||
@@ -426,12 +417,10 @@ nsBrowserContentHandler.prototype = {
|
||||
var fileParam = cmdLine.handleFlagWithParam("file", false);
|
||||
if (fileParam) {
|
||||
var file = cmdLine.resolveFile(fileParam);
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
var uri = ios.newFileURI(file);
|
||||
openWindow(null, this.chromeURL, "_blank",
|
||||
var fileURI = Services.io.newFileURI(file);
|
||||
openWindow(null, this.chromeURL, "_blank",
|
||||
"chrome,dialog=no,all" + this.getFeatures(cmdLine),
|
||||
uri.spec);
|
||||
fileURI.spec);
|
||||
cmdLine.preventDefault = true;
|
||||
}
|
||||
|
||||
@@ -468,8 +457,7 @@ nsBrowserContentHandler.prototype = {
|
||||
/* nsIBrowserHandler */
|
||||
|
||||
get defaultArgs() {
|
||||
var prefb = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(nsIPrefBranch);
|
||||
var prefb = Services.prefs;
|
||||
|
||||
if (!gFirstWindow) {
|
||||
gFirstWindow = true;
|
||||
@@ -685,9 +673,7 @@ nsDefaultCommandLineHandler.prototype = {
|
||||
if (!this._haveProfile) {
|
||||
try {
|
||||
// This will throw when a profile has not been selected.
|
||||
var fl = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties);
|
||||
var dir = fl.get("ProfD", Components.interfaces.nsILocalFile);
|
||||
var dir = Services.dirsvc.get("ProfD", Components.interfaces.nsILocalFile);
|
||||
this._haveProfile = true;
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -91,20 +91,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
|
||||
"resource://gre/modules/LoginManagerParent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
|
||||
return Services.strings.createBundle('chrome://branding/locale/brand.properties');
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
|
||||
return Services.strings.createBundle('chrome://browser/locale/browser.properties');
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
|
||||
"resource:///modules/FormValidationHandler.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
|
||||
"resource://gre/modules/AddonWatcher.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
@@ -132,9 +118,24 @@ if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
"resource:///modules/ContentCrashHandlers.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
|
||||
return Services.strings.createBundle('chrome://branding/locale/brand.properties');
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
|
||||
return Services.strings.createBundle('chrome://browser/locale/browser.properties');
|
||||
});
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
|
||||
"resource:///modules/FormValidationHandler.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
|
||||
"resource:///modules/ReaderParent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
|
||||
"resource://gre/modules/AddonWatcher.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"../../../../testing/mochitest/browser.eslintrc"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
|
||||
[browser_bug538331.js]
|
||||
@@ -0,0 +1,426 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* 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 PREF_POSTUPDATE = "app.update.postupdate";
|
||||
const PREF_MSTONE = "browser.startup.homepage_override.mstone";
|
||||
const PREF_OVERRIDE_URL = "startup.homepage_override_url";
|
||||
|
||||
const DEFAULT_PREF_URL = "http://pref.example.com/";
|
||||
const DEFAULT_UPDATE_URL = "http://example.com/";
|
||||
|
||||
const XML_EMPTY = "<?xml version=\"1.0\"?><updates xmlns=" +
|
||||
"\"http://www.mozilla.org/2005/app-update\"></updates>";
|
||||
|
||||
const XML_PREFIX = "<updates xmlns=\"http://www.mozilla.org/2005/app-update\"" +
|
||||
"><update appVersion=\"1.0\" buildID=\"20080811053724\" " +
|
||||
"channel=\"nightly\" displayVersion=\"Version 1.0\" " +
|
||||
"extensionVersion=\"1.0\" installDate=\"1238441400314\" " +
|
||||
"isCompleteUpdate=\"true\" name=\"Update Test 1.0\" " +
|
||||
"serviceURL=\"https://example.com/\" showNeverForVersion=" +
|
||||
"\"false\" showPrompt=\"false\" type=" +
|
||||
"\"minor\" version=\"version 1.0\" detailsURL=" +
|
||||
"\"http://example.com/\" previousAppVersion=\"1.0\" " +
|
||||
"statusText=\"The Update was successfully installed\" " +
|
||||
"foregroundDownload=\"true\"";
|
||||
|
||||
const XML_SUFFIX = "><patch type=\"complete\" URL=\"http://example.com/\" " +
|
||||
"hashFunction=\"MD5\" hashValue=" +
|
||||
"\"6232cd43a1c77e30191c53a329a3f99d\" size=\"775\" " +
|
||||
"selected=\"true\" state=\"succeeded\"/></update></updates>";
|
||||
|
||||
// nsBrowserContentHandler.js defaultArgs tests
|
||||
const BCH_TESTS = [
|
||||
{
|
||||
description: "no mstone change and no update",
|
||||
noPostUpdatePref: true,
|
||||
noMstoneChange: true
|
||||
}, {
|
||||
description: "mstone changed and no update",
|
||||
noPostUpdatePref: true,
|
||||
prefURL: DEFAULT_PREF_URL
|
||||
}, {
|
||||
description: "no mstone change and update with 'showURL' for actions",
|
||||
actions: "showURL",
|
||||
noMstoneChange: true
|
||||
}, {
|
||||
description: "update without actions",
|
||||
prefURL: DEFAULT_PREF_URL
|
||||
}, {
|
||||
description: "update with 'showURL' for actions",
|
||||
actions: "showURL",
|
||||
prefURL: DEFAULT_PREF_URL
|
||||
}, {
|
||||
description: "update with 'showURL' for actions and openURL",
|
||||
actions: "showURL",
|
||||
openURL: DEFAULT_UPDATE_URL
|
||||
}, {
|
||||
description: "update with 'showURL showAlert' for actions",
|
||||
actions: "showAlert showURL",
|
||||
prefURL: DEFAULT_PREF_URL
|
||||
}, {
|
||||
description: "update with 'showAlert showURL' for actions and openURL",
|
||||
actions: "showAlert showURL",
|
||||
openURL: DEFAULT_UPDATE_URL
|
||||
}, {
|
||||
description: "update with 'showURL showNotification' for actions",
|
||||
actions: "showURL showNotification",
|
||||
prefURL: DEFAULT_PREF_URL
|
||||
}, {
|
||||
description: "update with 'showNotification showURL' for actions and " +
|
||||
"openURL",
|
||||
actions: "showNotification showURL",
|
||||
openURL: DEFAULT_UPDATE_URL
|
||||
}, {
|
||||
description: "update with 'showAlert showURL showNotification' for actions",
|
||||
actions: "showAlert showURL showNotification",
|
||||
prefURL: DEFAULT_PREF_URL
|
||||
}, {
|
||||
description: "update with 'showNotification showURL showAlert' for " +
|
||||
"actions and openURL",
|
||||
actions: "showNotification showURL showAlert",
|
||||
openURL: DEFAULT_UPDATE_URL
|
||||
}, {
|
||||
description: "update with 'showAlert' for actions",
|
||||
actions: "showAlert"
|
||||
}, {
|
||||
description: "update with 'showAlert showNotification' for actions",
|
||||
actions: "showAlert showNotification"
|
||||
}, {
|
||||
description: "update with 'showNotification' for actions",
|
||||
actions: "showNotification"
|
||||
}, {
|
||||
description: "update with 'showNotification showAlert' for actions",
|
||||
actions: "showNotification showAlert"
|
||||
}, {
|
||||
description: "update with 'silent' for actions",
|
||||
actions: "silent"
|
||||
}, {
|
||||
description: "update with 'silent showURL showAlert showNotification' " +
|
||||
"for actions and openURL",
|
||||
actions: "silent showURL showAlert showNotification"
|
||||
}
|
||||
];
|
||||
|
||||
var gOriginalMStone;
|
||||
var gOriginalOverrideURL;
|
||||
|
||||
this.__defineGetter__("gBG", function() {
|
||||
delete this.gBG;
|
||||
return this.gBG = Cc["@mozilla.org/browser/browserglue;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
});
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Reset the startup page pref since it may have been set by other tests
|
||||
// and we will assume it is default.
|
||||
Services.prefs.clearUserPref('browser.startup.page');
|
||||
|
||||
if (gPrefService.prefHasUserValue(PREF_MSTONE)) {
|
||||
gOriginalMStone = gPrefService.getCharPref(PREF_MSTONE);
|
||||
}
|
||||
|
||||
if (gPrefService.prefHasUserValue(PREF_OVERRIDE_URL)) {
|
||||
gOriginalOverrideURL = gPrefService.getCharPref(PREF_OVERRIDE_URL);
|
||||
}
|
||||
|
||||
testDefaultArgs();
|
||||
}
|
||||
|
||||
var gWindowCatcher = {
|
||||
windowsOpen: 0,
|
||||
finishCalled: false,
|
||||
start: function() {
|
||||
Services.ww.registerNotification(this);
|
||||
},
|
||||
|
||||
finish: function(aFunc) {
|
||||
Services.ww.unregisterNotification(this);
|
||||
this.finishFunc = aFunc;
|
||||
if (this.windowsOpen > 0)
|
||||
return;
|
||||
|
||||
this.finishFunc();
|
||||
},
|
||||
|
||||
closeWindow: function (win) {
|
||||
info("window catcher closing window: " + win.document.documentURI);
|
||||
win.close();
|
||||
this.windowsOpen--;
|
||||
if (this.finishFunc) {
|
||||
this.finish(this.finishFunc);
|
||||
}
|
||||
},
|
||||
|
||||
windowLoad: function (win) {
|
||||
executeSoon(this.closeWindow.bind(this, win));
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic != "domwindowopened")
|
||||
return;
|
||||
|
||||
this.windowsOpen++;
|
||||
let win = subject.QueryInterface(Ci.nsIDOMWindow);
|
||||
info("window catcher caught window opening: " + win.document.documentURI);
|
||||
win.addEventListener("load", function () {
|
||||
win.removeEventListener("load", arguments.callee, false);
|
||||
gWindowCatcher.windowLoad(win);
|
||||
}, false);
|
||||
}
|
||||
};
|
||||
|
||||
function finish_test()
|
||||
{
|
||||
// Reset browser.startup.homepage_override.mstone to the original value or
|
||||
// clear it if it didn't exist.
|
||||
if (gOriginalMStone) {
|
||||
gPrefService.setCharPref(PREF_MSTONE, gOriginalMStone);
|
||||
} else if (gPrefService.prefHasUserValue(PREF_MSTONE)) {
|
||||
gPrefService.clearUserPref(PREF_MSTONE);
|
||||
}
|
||||
|
||||
// Reset startup.homepage_override_url to the original value or clear it if
|
||||
// it didn't exist.
|
||||
if (gOriginalOverrideURL) {
|
||||
gPrefService.setCharPref(PREF_OVERRIDE_URL, gOriginalOverrideURL);
|
||||
} else if (gPrefService.prefHasUserValue(PREF_OVERRIDE_URL)) {
|
||||
gPrefService.clearUserPref(PREF_OVERRIDE_URL);
|
||||
}
|
||||
|
||||
writeUpdatesToXMLFile(XML_EMPTY);
|
||||
reloadUpdateManagerData();
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
// Test the defaultArgs returned by nsBrowserContentHandler after an update
|
||||
function testDefaultArgs()
|
||||
{
|
||||
// Clear any pre-existing override in defaultArgs that are hanging around.
|
||||
// This will also set the browser.startup.homepage_override.mstone preference
|
||||
// if it isn't already set.
|
||||
Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
|
||||
|
||||
let originalMstone = gPrefService.getCharPref(PREF_MSTONE);
|
||||
|
||||
gPrefService.setCharPref(PREF_OVERRIDE_URL, DEFAULT_PREF_URL);
|
||||
|
||||
writeUpdatesToXMLFile(XML_EMPTY);
|
||||
reloadUpdateManagerData();
|
||||
|
||||
for (let i = 0; i < BCH_TESTS.length; i++) {
|
||||
let test = BCH_TESTS[i];
|
||||
ok(true, "Test nsBrowserContentHandler " + (i + 1) + ": " + test.description);
|
||||
|
||||
if (test.actions) {
|
||||
let actionsXML = " actions=\"" + test.actions + "\"";
|
||||
if (test.openURL) {
|
||||
actionsXML += " openURL=\"" + test.openURL + "\"";
|
||||
}
|
||||
writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
|
||||
} else {
|
||||
writeUpdatesToXMLFile(XML_EMPTY);
|
||||
}
|
||||
|
||||
reloadUpdateManagerData();
|
||||
|
||||
let noOverrideArgs = Cc["@mozilla.org/browser/clh;1"].
|
||||
getService(Ci.nsIBrowserHandler).defaultArgs;
|
||||
|
||||
let overrideArgs = "";
|
||||
if (test.prefURL) {
|
||||
overrideArgs = test.prefURL;
|
||||
} else if (test.openURL) {
|
||||
overrideArgs = test.openURL;
|
||||
}
|
||||
|
||||
if (overrideArgs == "" && noOverrideArgs) {
|
||||
overrideArgs = noOverrideArgs;
|
||||
} else if (noOverrideArgs) {
|
||||
overrideArgs += "|" + noOverrideArgs;
|
||||
}
|
||||
|
||||
if (test.noMstoneChange === undefined) {
|
||||
gPrefService.setCharPref(PREF_MSTONE, "PreviousMilestone");
|
||||
}
|
||||
|
||||
if (test.noPostUpdatePref == undefined) {
|
||||
gPrefService.setBoolPref(PREF_POSTUPDATE, true);
|
||||
}
|
||||
|
||||
let defaultArgs = Cc["@mozilla.org/browser/clh;1"].
|
||||
getService(Ci.nsIBrowserHandler).defaultArgs;
|
||||
is(defaultArgs, overrideArgs, "correct value returned by defaultArgs");
|
||||
|
||||
if (test.noMstoneChange === undefined || test.noMstoneChange != true) {
|
||||
let newMstone = gPrefService.getCharPref(PREF_MSTONE);
|
||||
is(originalMstone, newMstone, "preference " + PREF_MSTONE +
|
||||
" should have been updated");
|
||||
}
|
||||
|
||||
if (gPrefService.prefHasUserValue(PREF_POSTUPDATE)) {
|
||||
gPrefService.clearUserPref(PREF_POSTUPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
testShowNotification();
|
||||
}
|
||||
|
||||
// nsBrowserGlue.js _showUpdateNotification notification tests
|
||||
const BG_NOTIFY_TESTS = [
|
||||
{
|
||||
description: "'silent showNotification' actions should not display a notification",
|
||||
actions: "silent showNotification"
|
||||
}, {
|
||||
description: "'showNotification' for actions should display a notification",
|
||||
actions: "showNotification"
|
||||
}, {
|
||||
description: "no actions and empty updates.xml",
|
||||
}, {
|
||||
description: "'showAlert' for actions should not display a notification",
|
||||
actions: "showAlert"
|
||||
}, {
|
||||
// This test MUST be the last test in the array to test opening the url
|
||||
// provided by the updates.xml.
|
||||
description: "'showNotification' for actions with custom notification " +
|
||||
"attributes should display a notification",
|
||||
actions: "showNotification",
|
||||
notificationText: "notification text",
|
||||
notificationURL: DEFAULT_UPDATE_URL,
|
||||
notificationButtonLabel: "button label",
|
||||
notificationButtonAccessKey: "b"
|
||||
}
|
||||
];
|
||||
|
||||
// Test showing a notification after an update
|
||||
// _showUpdateNotification in nsBrowserGlue.js
|
||||
function testShowNotification()
|
||||
{
|
||||
let notifyBox = document.getElementById("high-priority-global-notificationbox");
|
||||
|
||||
// Catches any windows opened by these tests (e.g. alert windows) and closes
|
||||
// them
|
||||
gWindowCatcher.start();
|
||||
|
||||
for (let i = 0; i < BG_NOTIFY_TESTS.length; i++) {
|
||||
let test = BG_NOTIFY_TESTS[i];
|
||||
ok(true, "Test showNotification " + (i + 1) + ": " + test.description);
|
||||
|
||||
if (test.actions) {
|
||||
let actionsXML = " actions=\"" + test.actions + "\"";
|
||||
if (test.notificationText) {
|
||||
actionsXML += " notificationText=\"" + test.notificationText + "\"";
|
||||
}
|
||||
if (test.notificationURL) {
|
||||
actionsXML += " notificationURL=\"" + test.notificationURL + "\"";
|
||||
}
|
||||
if (test.notificationButtonLabel) {
|
||||
actionsXML += " notificationButtonLabel=\"" + test.notificationButtonLabel + "\"";
|
||||
}
|
||||
if (test.notificationButtonAccessKey) {
|
||||
actionsXML += " notificationButtonAccessKey=\"" + test.notificationButtonAccessKey + "\"";
|
||||
}
|
||||
writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
|
||||
} else {
|
||||
writeUpdatesToXMLFile(XML_EMPTY);
|
||||
}
|
||||
|
||||
reloadUpdateManagerData();
|
||||
gPrefService.setBoolPref(PREF_POSTUPDATE, true);
|
||||
|
||||
gBG.observe(null, "browser-glue-test", "post-update-notification");
|
||||
|
||||
let updateBox = notifyBox.getNotificationWithValue("post-update-notification");
|
||||
if (test.actions && test.actions.indexOf("showNotification") != -1 &&
|
||||
test.actions.indexOf("silent") == -1) {
|
||||
ok(updateBox, "Update notification box should have been displayed");
|
||||
if (updateBox) {
|
||||
if (test.notificationText) {
|
||||
is(updateBox.label, test.notificationText, "Update notification box " +
|
||||
"should have the label provided by the update");
|
||||
}
|
||||
if (test.notificationButtonLabel) {
|
||||
var button = updateBox.getElementsByTagName("button").item(0);
|
||||
is(button.label, test.notificationButtonLabel, "Update notification " +
|
||||
"box button should have the label provided by the update");
|
||||
if (test.notificationButtonAccessKey) {
|
||||
let accessKey = button.getAttribute("accesskey");
|
||||
is(accessKey, test.notificationButtonAccessKey, "Update " +
|
||||
"notification box button should have the accesskey " +
|
||||
"provided by the update");
|
||||
}
|
||||
}
|
||||
// The last test opens an url and verifies the url from the updates.xml
|
||||
// is correct.
|
||||
if (i == (BG_NOTIFY_TESTS.length - 1)) {
|
||||
// Wait for any windows caught by the windowcatcher to close
|
||||
gWindowCatcher.finish(function () {
|
||||
BrowserTestUtils.waitForNewTab(gBrowser).then(testNotificationURL);
|
||||
button.click();
|
||||
});
|
||||
} else {
|
||||
notifyBox.removeAllNotifications(true);
|
||||
}
|
||||
} else if (i == (BG_NOTIFY_TESTS.length - 1)) {
|
||||
// If updateBox is null the test has already reported errors so bail
|
||||
finish_test();
|
||||
}
|
||||
} else {
|
||||
ok(!updateBox, "Update notification box should not have been displayed");
|
||||
}
|
||||
|
||||
let prefHasUserValue = gPrefService.prefHasUserValue(PREF_POSTUPDATE);
|
||||
is(prefHasUserValue, false, "preference " + PREF_POSTUPDATE +
|
||||
" shouldn't have a user value");
|
||||
}
|
||||
}
|
||||
|
||||
// Test opening the url provided by the updates.xml in the last test
|
||||
function testNotificationURL()
|
||||
{
|
||||
ok(true, "Test testNotificationURL: clicking the notification button " +
|
||||
"opened the url specified by the update");
|
||||
let href = gBrowser.currentURI.spec;
|
||||
let expectedURL = BG_NOTIFY_TESTS[BG_NOTIFY_TESTS.length - 1].notificationURL;
|
||||
is(href, expectedURL, "The url opened from the notification should be the " +
|
||||
"url provided by the update");
|
||||
gBrowser.removeCurrentTab();
|
||||
window.focus();
|
||||
finish_test();
|
||||
}
|
||||
|
||||
/* Reloads the update metadata from disk */
|
||||
function reloadUpdateManagerData()
|
||||
{
|
||||
Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager).
|
||||
QueryInterface(Ci.nsIObserver).observe(null, "um-reload-update-data", "");
|
||||
}
|
||||
|
||||
|
||||
function writeUpdatesToXMLFile(aText)
|
||||
{
|
||||
const PERMS_FILE = 0o644;
|
||||
|
||||
const MODE_WRONLY = 0x02;
|
||||
const MODE_CREATE = 0x08;
|
||||
const MODE_TRUNCATE = 0x20;
|
||||
|
||||
let file = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties).
|
||||
get("UpdRootD", Ci.nsIFile);
|
||||
file.append("updates.xml");
|
||||
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
if (!file.exists()) {
|
||||
file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
|
||||
}
|
||||
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
|
||||
fos.write(aText, aText.length);
|
||||
fos.close();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
|
||||
<ShortName>Google</ShortName>
|
||||
<Description>override-de-DE</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Url type="text/html" method="GET" template="http://searchtest.local">
|
||||
<Param name="search" value="{searchTerms}"/>
|
||||
</Url>
|
||||
</SearchPlugin>
|
||||
@@ -0,0 +1,58 @@
|
||||
# Distribution Configuration File
|
||||
# Test of distribution preferences
|
||||
|
||||
[Global]
|
||||
id=disttest
|
||||
version=1.0
|
||||
about=Test distribution file
|
||||
about.en-US=Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè
|
||||
|
||||
[Preferences]
|
||||
distribution.test.string="Test String"
|
||||
distribution.test.string.noquotes=Test String
|
||||
distribution.test.int=777
|
||||
distribution.test.bool.true=true
|
||||
distribution.test.bool.false=false
|
||||
distribution.test.empty=
|
||||
|
||||
distribution.test.pref.locale="%LOCALE%"
|
||||
distribution.test.pref.language.reset="Preference Set"
|
||||
distribution.test.pref.locale.reset="Preference Set"
|
||||
distribution.test.pref.locale.set="Preference Set"
|
||||
distribution.test.pref.language.set="Preference Set"
|
||||
|
||||
[Preferences-en]
|
||||
distribution.test.pref.language.en="en"
|
||||
distribution.test.pref.language.reset=
|
||||
distribution.test.pref.language.set="Language Set"
|
||||
distribution.test.pref.locale.set="Language Set"
|
||||
|
||||
[Preferences-en-US]
|
||||
distribution.test.pref.locale.en-US="en-US"
|
||||
distribution.test.pref.locale.reset=
|
||||
distribution.test.pref.locale.set="Locale Set"
|
||||
|
||||
|
||||
[Preferences-de]
|
||||
distribution.test.pref.language.de="de"
|
||||
|
||||
[LocalizablePreferences]
|
||||
distribution.test.locale="%LOCALE%"
|
||||
distribution.test.language.reset="Preference Set"
|
||||
distribution.test.locale.reset="Preference Set"
|
||||
distribution.test.locale.set="Preference Set"
|
||||
distribution.test.language.set="Preference Set"
|
||||
|
||||
[LocalizablePreferences-en]
|
||||
distribution.test.language.en="en"
|
||||
distribution.test.language.reset=
|
||||
distribution.test.language.set="Language Set"
|
||||
distribution.test.locale.set="Language Set"
|
||||
|
||||
[LocalizablePreferences-en-US]
|
||||
distribution.test.locale.en-US="en-US"
|
||||
distribution.test.locale.reset=
|
||||
distribution.test.locale.set="Locale Set"
|
||||
|
||||
[LocalizablePreferences-de]
|
||||
distribution.test.language.de="de"
|
||||
@@ -0,0 +1,157 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that preferences are properly set by distribution.ini
|
||||
*/
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
var Cr = Components.results;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/LoadContextInfo.jsm");
|
||||
|
||||
// Import common head.
|
||||
var commonFile = do_get_file("../../../../toolkit/components/places/tests/head_common.js", false);
|
||||
if (commonFile) {
|
||||
let uri = Services.io.newFileURI(commonFile);
|
||||
Services.scriptloader.loadSubScript(uri.spec, this);
|
||||
}
|
||||
|
||||
const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
|
||||
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
|
||||
|
||||
/**
|
||||
* Copy the engine-distribution.xml engine to a fake distribution
|
||||
* created in the profile, and registered with the directory service.
|
||||
* Create an empty en-US directory to make sure it isn't used.
|
||||
*/
|
||||
function installDistributionEngine() {
|
||||
const XRE_APP_DISTRIBUTION_DIR = "XREAppDist";
|
||||
|
||||
const gProfD = do_get_profile().QueryInterface(Ci.nsILocalFile);
|
||||
|
||||
let dir = gProfD.clone();
|
||||
dir.append("distribution");
|
||||
let distDir = dir.clone();
|
||||
|
||||
dir.append("searchplugins");
|
||||
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
dir.append("locale");
|
||||
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
let localeDir = dir.clone();
|
||||
|
||||
dir.append("en-US");
|
||||
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
localeDir.append("de-DE");
|
||||
localeDir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
do_get_file("data/engine-de-DE.xml").copyTo(localeDir, "engine-de-DE.xml");
|
||||
|
||||
Services.dirsvc.registerProvider({
|
||||
getFile: function(aProp, aPersistent) {
|
||||
aPersistent.value = true;
|
||||
if (aProp == XRE_APP_DISTRIBUTION_DIR)
|
||||
return distDir.clone();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
// Set special pref to load distribution.ini from the profile folder.
|
||||
Services.prefs.setBoolPref("distribution.testing.loadFromProfile", true);
|
||||
|
||||
// Copy distribution.ini file to the profile dir.
|
||||
let distroDir = gProfD.clone();
|
||||
distroDir.leafName = "distribution";
|
||||
let iniFile = distroDir.clone();
|
||||
iniFile.append("distribution.ini");
|
||||
if (iniFile.exists()) {
|
||||
iniFile.remove(false);
|
||||
print("distribution.ini already exists, did some test forget to cleanup?");
|
||||
}
|
||||
|
||||
let testDistributionFile = gTestDir.clone();
|
||||
testDistributionFile.append("distribution.ini");
|
||||
testDistributionFile.copyTo(distroDir, "distribution.ini");
|
||||
Assert.ok(testDistributionFile.exists());
|
||||
|
||||
installDistributionEngine();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
do_register_cleanup(function () {
|
||||
// Remove the distribution dir, even if the test failed, otherwise all
|
||||
// next tests will use it.
|
||||
let distDir = gProfD.clone();
|
||||
distDir.append("distribution");
|
||||
distDir.remove(true);
|
||||
Assert.ok(!distDir.exists());
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
// Force distribution.
|
||||
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
|
||||
glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
|
||||
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.id"), "disttest");
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.version"), "1.0");
|
||||
Assert.equal(Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString).data, "Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè");
|
||||
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.string"), "Test String");
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.string.noquotes"), "Test String");
|
||||
Assert.equal(Services.prefs.getIntPref("distribution.test.int"), 777);
|
||||
Assert.equal(Services.prefs.getBoolPref("distribution.test.bool.true"), true);
|
||||
Assert.equal(Services.prefs.getBoolPref("distribution.test.bool.false"), false);
|
||||
|
||||
Assert.throws(() => Services.prefs.getCharPref("distribution.test.empty"));
|
||||
Assert.throws(() => Services.prefs.getIntPref("distribution.test.empty"));
|
||||
Assert.throws(() => Services.prefs.getBoolPref("distribution.test.empty"));
|
||||
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.locale"), "en-US");
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.language.en"), "en");
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.locale.en-US"), "en-US");
|
||||
Assert.throws(() => Services.prefs.getCharPref("distribution.test.pref.language.de"));
|
||||
// This value was never set because of the empty language specific pref
|
||||
Assert.throws(() => Services.prefs.getCharPref("distribution.test.pref.language.reset"));
|
||||
// This value was never set because of the empty locale specific pref
|
||||
Assert.throws(() => Services.prefs.getCharPref("distribution.test.pref.locale.reset"));
|
||||
// This value was overridden by a locale specific setting
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.locale.set"), "Locale Set");
|
||||
// This value was overridden by a language specific setting
|
||||
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.language.set"), "Language Set");
|
||||
// Language should not override locale
|
||||
Assert.notEqual(Services.prefs.getCharPref("distribution.test.pref.locale.set"), "Language Set");
|
||||
|
||||
Assert.equal(Services.prefs.getComplexValue("distribution.test.locale", Ci.nsIPrefLocalizedString).data, "en-US");
|
||||
Assert.equal(Services.prefs.getComplexValue("distribution.test.language.en", Ci.nsIPrefLocalizedString).data, "en");
|
||||
Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.en-US", Ci.nsIPrefLocalizedString).data, "en-US");
|
||||
Assert.throws(() => Services.prefs.getComplexValue("distribution.test.language.de", Ci.nsIPrefLocalizedString));
|
||||
// This value was never set because of the empty language specific pref
|
||||
Assert.throws(() => Services.prefs.getComplexValue("distribution.test.language.reset", Ci.nsIPrefLocalizedString));
|
||||
// This value was never set because of the empty locale specific pref
|
||||
Assert.throws(() => Services.prefs.getComplexValue("distribution.test.locale.reset", Ci.nsIPrefLocalizedString));
|
||||
// This value was overridden by a locale specific setting
|
||||
Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Locale Set");
|
||||
// This value was overridden by a language specific setting
|
||||
Assert.equal(Services.prefs.getComplexValue("distribution.test.language.set", Ci.nsIPrefLocalizedString).data, "Language Set");
|
||||
// Language should not override locale
|
||||
Assert.notEqual(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Language Set");
|
||||
|
||||
do_test_pending();
|
||||
|
||||
Services.prefs.setCharPref("distribution.searchplugins.defaultLocale", "de-DE");
|
||||
|
||||
Services.search.init(function() {
|
||||
Assert.equal(Services.search.isInitialized, true);
|
||||
var engine = Services.search.getEngineByName("Google");
|
||||
Assert.equal(engine.description, "override-de-DE");
|
||||
do_test_finished();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
support-files =
|
||||
distribution.ini
|
||||
data/engine-de-DE.xml
|
||||
|
||||
[test_distribution.js]
|
||||
@@ -41,7 +41,7 @@ function ProtocolHandler(aScheme, aFlags)
|
||||
ProtocolHandler.prototype =
|
||||
{
|
||||
defaultPort: -1,
|
||||
allowPort: function() false,
|
||||
allowPort: () => false,
|
||||
newURI: function(aSpec, aCharset, aBaseURI)
|
||||
{
|
||||
let uri = Cc["@mozilla.org/network/standard-url;1"].
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@
|
||||
// Rules from the mozilla plugin
|
||||
"mozilla/balanced-listeners": 2,
|
||||
"mozilla/components-imports": 1,
|
||||
"mozilla/import-globals-from": 1,
|
||||
"mozilla/import-headjs-globals": 1,
|
||||
"mozilla/mark-test-function-used": 1,
|
||||
"mozilla/no-aArgs": 1,
|
||||
@@ -96,7 +97,7 @@
|
||||
// rule is a better rule to check this.
|
||||
"max-depth": 0,
|
||||
// Maximum length of a line.
|
||||
"max-len": [1, 80],
|
||||
"max-len": [2, 80, 2, {"ignoreUrls": true, "ignorePattern": "\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}],
|
||||
// Maximum depth callbacks can be nested.
|
||||
"max-nested-callbacks": [2, 3],
|
||||
// Don't limit the number of parameters that can be used in a function.
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* globals ViewHelpers, Task, AnimationsPanel, promise, EventEmitter,
|
||||
AnimationsFront */
|
||||
|
||||
/* animation-panel.js is loaded in the same scope but we don't use
|
||||
import-globals-from to avoid infinite loops since animation-panel.js already
|
||||
imports globals from animation-controller.js */
|
||||
/* globals AnimationsPanel */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -16,10 +20,8 @@ Cu.import("resource://gre/modules/Console.jsm");
|
||||
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
|
||||
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/shared/event-emitter");
|
||||
loader.lazyRequireGetter(this, "AnimationsFront",
|
||||
"devtools/server/actors/animation", true);
|
||||
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||
loader.lazyRequireGetter(this, "AnimationsFront", "devtools/server/actors/animation", true);
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/animationinspector.properties";
|
||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
@@ -127,6 +129,7 @@ var getServerTraits = Task.async(function*(target) {
|
||||
*/
|
||||
var AnimationsController = {
|
||||
PLAYERS_UPDATED_EVENT: "players-updated",
|
||||
ALL_ANIMATIONS_TOGGLED_EVENT: "all-animations-toggled",
|
||||
|
||||
initialize: Task.async(function*() {
|
||||
if (this.initialized) {
|
||||
@@ -174,7 +177,7 @@ var AnimationsController = {
|
||||
this.destroyed = promise.defer();
|
||||
|
||||
this.stopListeners();
|
||||
yield this.destroyAnimationPlayers();
|
||||
this.destroyAnimationPlayers();
|
||||
this.nodeFront = null;
|
||||
|
||||
if (this.animationsFront) {
|
||||
@@ -221,17 +224,18 @@ var AnimationsController = {
|
||||
return;
|
||||
}
|
||||
|
||||
this.nodeFront = gInspector.selection.nodeFront;
|
||||
let done = gInspector.updating("animationscontroller");
|
||||
|
||||
if (!gInspector.selection.isConnected() ||
|
||||
!gInspector.selection.isElementNode()) {
|
||||
yield this.destroyAnimationPlayers();
|
||||
!gInspector.selection.isElementNode() ||
|
||||
gInspector.selection.isPseudoElementNode()) {
|
||||
this.destroyAnimationPlayers();
|
||||
this.emit(this.PLAYERS_UPDATED_EVENT);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
this.nodeFront = gInspector.selection.nodeFront;
|
||||
yield this.refreshAnimationPlayers(this.nodeFront);
|
||||
this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);
|
||||
|
||||
@@ -246,7 +250,9 @@ var AnimationsController = {
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
return this.animationsFront.toggleAll().catch(e => console.error(e));
|
||||
return this.animationsFront.toggleAll()
|
||||
.then(() => this.emit(this.ALL_ANIMATIONS_TOGGLED_EVENT, this))
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -319,7 +325,7 @@ var AnimationsController = {
|
||||
animationPlayers: [],
|
||||
|
||||
refreshAnimationPlayers: Task.async(function*(nodeFront) {
|
||||
yield this.destroyAnimationPlayers();
|
||||
this.destroyAnimationPlayers();
|
||||
|
||||
this.animationPlayers = yield this.animationsFront
|
||||
.getAnimationPlayersForNode(nodeFront);
|
||||
@@ -332,7 +338,7 @@ var AnimationsController = {
|
||||
}
|
||||
}),
|
||||
|
||||
onAnimationMutations: Task.async(function*(changes) {
|
||||
onAnimationMutations: function(changes) {
|
||||
// Insert new players into this.animationPlayers when new animations are
|
||||
// added.
|
||||
for (let {type, player} of changes) {
|
||||
@@ -341,7 +347,6 @@ var AnimationsController = {
|
||||
}
|
||||
|
||||
if (type === "removed") {
|
||||
yield player.release();
|
||||
let index = this.animationPlayers.indexOf(player);
|
||||
this.animationPlayers.splice(index, 1);
|
||||
}
|
||||
@@ -349,7 +354,7 @@ var AnimationsController = {
|
||||
|
||||
// Let the UI know the list has been updated.
|
||||
this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the latest known current time of document.timeline.
|
||||
@@ -370,19 +375,9 @@ var AnimationsController = {
|
||||
return time;
|
||||
},
|
||||
|
||||
destroyAnimationPlayers: Task.async(function*() {
|
||||
// Let the server know that we're not interested in receiving updates about
|
||||
// players for the current node. We're either being destroyed or a new node
|
||||
// has been selected.
|
||||
if (this.traits.hasMutationEvents) {
|
||||
yield this.animationsFront.stopAnimationPlayerUpdates();
|
||||
}
|
||||
|
||||
for (let front of this.animationPlayers) {
|
||||
yield front.release();
|
||||
}
|
||||
destroyAnimationPlayers: function() {
|
||||
this.animationPlayers = [];
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.decorate(AnimationsController);
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
<div id="timeline-toolbar" class="theme-toolbar">
|
||||
<button id="rewind-timeline" standalone="true" class="devtools-button"></button>
|
||||
<button id="pause-resume-timeline" standalone="true" class="devtools-button pause-button paused"></button>
|
||||
<span id="timeline-rate"></span>
|
||||
<span id="timeline-current-time" class="label"></span>
|
||||
<span id="timeline-rate" standalone="true" class="devtools-button"></span>
|
||||
<span id="timeline-current-time" class="label devtools-toolbarbutton"></span>
|
||||
</div>
|
||||
<div id="players"></div>
|
||||
<div id="error-message">
|
||||
<p>&invalidElement;</p>
|
||||
<p id="error-type"></p>
|
||||
<p>&selectElement;</p>
|
||||
<button id="element-picker" standalone="true" class="devtools-button"></button>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* globals AnimationsController, document, promise, gToolbox, gInspector */
|
||||
|
||||
/* import-globals-from animation-controller.js */
|
||||
/* globals document */
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -41,6 +43,9 @@ var AnimationsPanel = {
|
||||
this.timelineCurrentTimeEl = $("#timeline-current-time");
|
||||
this.rateSelectorEl = $("#timeline-rate");
|
||||
|
||||
this.rewindTimelineButtonEl.setAttribute("title",
|
||||
L10N.getStr("timeline.rewindButtonTooltip"));
|
||||
|
||||
// If the server doesn't support toggling all animations at once, hide the
|
||||
// whole global toolbar.
|
||||
if (!AnimationsController.traits.hasToggleAll) {
|
||||
@@ -48,10 +53,10 @@ var AnimationsPanel = {
|
||||
}
|
||||
|
||||
// Binding functions that need to be called in scope.
|
||||
for (let functionName of ["onPickerStarted", "onPickerStopped",
|
||||
"refreshAnimationsUI", "toggleAll", "onTabNavigated",
|
||||
"onTimelineDataChanged", "playPauseTimeline", "rewindTimeline",
|
||||
"onRateChanged"]) {
|
||||
for (let functionName of ["onKeyDown", "onPickerStarted",
|
||||
"onPickerStopped", "refreshAnimationsUI", "onToggleAllClicked",
|
||||
"onTabNavigated", "onTimelineDataChanged", "onTimelinePlayClicked",
|
||||
"onTimelineRewindClicked", "onRateChanged"]) {
|
||||
this[functionName] = this[functionName].bind(this);
|
||||
}
|
||||
let hUtils = gToolbox.highlighterUtils;
|
||||
@@ -110,9 +115,13 @@ var AnimationsPanel = {
|
||||
gToolbox.on("picker-started", this.onPickerStarted);
|
||||
gToolbox.on("picker-stopped", this.onPickerStopped);
|
||||
|
||||
this.toggleAllButtonEl.addEventListener("click", this.toggleAll);
|
||||
this.playTimelineButtonEl.addEventListener("click", this.playPauseTimeline);
|
||||
this.rewindTimelineButtonEl.addEventListener("click", this.rewindTimeline);
|
||||
this.toggleAllButtonEl.addEventListener("click", this.onToggleAllClicked);
|
||||
this.playTimelineButtonEl.addEventListener(
|
||||
"click", this.onTimelinePlayClicked);
|
||||
this.rewindTimelineButtonEl.addEventListener(
|
||||
"click", this.onTimelineRewindClicked);
|
||||
|
||||
document.addEventListener("keydown", this.onKeyDown, false);
|
||||
|
||||
gToolbox.target.on("navigate", this.onTabNavigated);
|
||||
|
||||
@@ -132,9 +141,14 @@ var AnimationsPanel = {
|
||||
gToolbox.off("picker-started", this.onPickerStarted);
|
||||
gToolbox.off("picker-stopped", this.onPickerStopped);
|
||||
|
||||
this.toggleAllButtonEl.removeEventListener("click", this.toggleAll);
|
||||
this.playTimelineButtonEl.removeEventListener("click", this.playPauseTimeline);
|
||||
this.rewindTimelineButtonEl.removeEventListener("click", this.rewindTimeline);
|
||||
this.toggleAllButtonEl.removeEventListener("click",
|
||||
this.onToggleAllClicked);
|
||||
this.playTimelineButtonEl.removeEventListener("click",
|
||||
this.onTimelinePlayClicked);
|
||||
this.rewindTimelineButtonEl.removeEventListener("click",
|
||||
this.onTimelineRewindClicked);
|
||||
|
||||
document.removeEventListener("keydown", this.onKeyDown, false);
|
||||
|
||||
gToolbox.target.off("navigate", this.onTabNavigated);
|
||||
|
||||
@@ -146,6 +160,22 @@ var AnimationsPanel = {
|
||||
}
|
||||
},
|
||||
|
||||
onKeyDown: function(event) {
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
|
||||
// If the space key is pressed, it should toggle the play state of
|
||||
// the animations displayed in the panel, or of all the animations on
|
||||
// the page if the selected node does not have any animation on it.
|
||||
if (event.keyCode === keyEvent.DOM_VK_SPACE) {
|
||||
if (AnimationsController.animationPlayers.length > 0) {
|
||||
this.playPauseTimeline().catch(ex => console.error(ex));
|
||||
} else {
|
||||
this.toggleAll().catch(ex => console.error(ex));
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
togglePlayers: function(isVisible) {
|
||||
if (isVisible) {
|
||||
document.body.removeAttribute("empty");
|
||||
@@ -153,6 +183,9 @@ var AnimationsPanel = {
|
||||
} else {
|
||||
document.body.setAttribute("empty", "true");
|
||||
document.body.removeAttribute("timeline");
|
||||
$("#error-type").textContent = gInspector.selection.isPseudoElementNode()
|
||||
? L10N.getStr("panel.pseudoElementSelected")
|
||||
: L10N.getStr("panel.invalidElementSelected");
|
||||
}
|
||||
},
|
||||
|
||||
@@ -164,32 +197,53 @@ var AnimationsPanel = {
|
||||
this.pickerButtonEl.removeAttribute("checked");
|
||||
},
|
||||
|
||||
onToggleAllClicked: function() {
|
||||
this.toggleAll().catch(ex => console.error(ex));
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle (pause/play) all animations in the current target
|
||||
* and update the UI the toggleAll button.
|
||||
*/
|
||||
toggleAll: Task.async(function*() {
|
||||
this.toggleAllButtonEl.classList.toggle("paused");
|
||||
yield AnimationsController.toggleAll();
|
||||
}),
|
||||
|
||||
onTimelinePlayClicked: function() {
|
||||
this.playPauseTimeline().catch(ex => console.error(ex));
|
||||
},
|
||||
|
||||
/**
|
||||
* Depending on the state of the timeline either pause or play the animations
|
||||
* displayed in it.
|
||||
* If the animations are finished, this will play them from the start again.
|
||||
* If the animations are playing, this will pause them.
|
||||
* If the animations are paused, this will resume them.
|
||||
*
|
||||
* @return {Promise} Resolves when the playState is changed and the UI
|
||||
* is refreshed
|
||||
*/
|
||||
playPauseTimeline: function() {
|
||||
AnimationsController.toggleCurrentAnimations(this.timelineData.isMoving)
|
||||
.then(() => this.refreshAnimationsStateAndUI())
|
||||
.catch(e => console.error(e));
|
||||
return AnimationsController
|
||||
.toggleCurrentAnimations(this.timelineData.isMoving)
|
||||
.then(() => this.refreshAnimationsStateAndUI());
|
||||
},
|
||||
|
||||
onTimelineRewindClicked: function() {
|
||||
this.rewindTimeline().catch(ex => console.error(ex));
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the startTime of all current animations shown in the timeline and
|
||||
* pause them.
|
||||
*
|
||||
* @return {Promise} Resolves when currentTime is set and the UI is refreshed
|
||||
*/
|
||||
rewindTimeline: function() {
|
||||
AnimationsController.setCurrentTimeAll(0, true)
|
||||
.then(() => this.refreshAnimationsStateAndUI())
|
||||
.catch(e => console.error(e));
|
||||
return AnimationsController
|
||||
.setCurrentTimeAll(0, true)
|
||||
.then(() => this.refreshAnimationsStateAndUI());
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -199,7 +253,7 @@ var AnimationsPanel = {
|
||||
onRateChanged: function(e, rate) {
|
||||
AnimationsController.setPlaybackRateAll(rate)
|
||||
.then(() => this.refreshAnimationsStateAndUI())
|
||||
.catch(e => console.error(e));
|
||||
.catch(ex => console.error(ex));
|
||||
},
|
||||
|
||||
onTabNavigated: function() {
|
||||
@@ -212,6 +266,12 @@ var AnimationsPanel = {
|
||||
|
||||
this.playTimelineButtonEl.classList.toggle("paused", !isMoving);
|
||||
|
||||
let l10nPlayProperty = isMoving ? "timeline.resumedButtonTooltip" :
|
||||
"timeline.pausedButtonTooltip";
|
||||
|
||||
this.playTimelineButtonEl.setAttribute("title",
|
||||
L10N.getStr(l10nPlayProperty));
|
||||
|
||||
// If the timeline data changed as a result of the user dragging the
|
||||
// scrubber, then pause all animations and set their currentTimes.
|
||||
// (Note that we want server-side requests to be sequenced, so we only do
|
||||
@@ -227,11 +287,8 @@ var AnimationsPanel = {
|
||||
},
|
||||
|
||||
displayTimelineCurrentTime: function() {
|
||||
let {isMoving, isPaused, time} = this.timelineData;
|
||||
|
||||
if (isMoving || isPaused) {
|
||||
this.timelineCurrentTimeEl.textContent = formatStopwatchTime(time);
|
||||
}
|
||||
let {time} = this.timelineData;
|
||||
this.timelineCurrentTimeEl.textContent = formatStopwatchTime(time);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -251,8 +308,6 @@ var AnimationsPanel = {
|
||||
* the various components again.
|
||||
*/
|
||||
refreshAnimationsUI: Task.async(function*() {
|
||||
let done = gInspector.updating("animationspanel");
|
||||
|
||||
// Empty the whole panel first.
|
||||
this.togglePlayers(true);
|
||||
|
||||
@@ -271,12 +326,10 @@ var AnimationsPanel = {
|
||||
if (!AnimationsController.animationPlayers.length) {
|
||||
this.togglePlayers(false);
|
||||
this.emit(this.UI_UPDATED_EVENT);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit(this.UI_UPDATED_EVENT);
|
||||
done();
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const {
|
||||
createNode,
|
||||
TimeScale
|
||||
} = require("devtools/client/animationinspector/utils");
|
||||
const {createNode, TimeScale} = require("devtools/client/animationinspector/utils");
|
||||
const {Keyframes} = require("devtools/client/animationinspector/components/keyframes");
|
||||
|
||||
/**
|
||||
* UI component responsible for displaying detailed information for a given
|
||||
* animation.
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const {DomNodePreview} = require(
|
||||
"devtools/client/inspector/shared/dom-node-preview");
|
||||
const {DomNodePreview} = require("devtools/client/inspector/shared/dom-node-preview");
|
||||
|
||||
// Map dom node fronts by animation fronts so we don't have to get them from the
|
||||
// walker every time the timeline is refreshed.
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
|
||||
const {
|
||||
createNode,
|
||||
TimeScale
|
||||
} = require("devtools/client/animationinspector/utils");
|
||||
const {createNode, TimeScale} = require("devtools/client/animationinspector/utils");
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/animationinspector.properties";
|
||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
@@ -52,11 +55,10 @@ AnimationTimeBlock.prototype = {
|
||||
let {x, iterationW, delayX, delayW, negativeDelayW} =
|
||||
TimeScale.getAnimationDimensions(animation);
|
||||
|
||||
let iterations = createNode({
|
||||
createNode({
|
||||
parent: this.containerEl,
|
||||
attributes: {
|
||||
"class": state.type + " iterations" +
|
||||
(state.iterationCount ? "" : " infinite"),
|
||||
"class": "iterations" + (state.iterationCount ? "" : " infinite"),
|
||||
// Individual iterations are represented by setting the size of the
|
||||
// repeating linear-gradient.
|
||||
"style": `left:${x}%;
|
||||
@@ -66,16 +68,20 @@ AnimationTimeBlock.prototype = {
|
||||
});
|
||||
|
||||
// The animation name is displayed over the iterations.
|
||||
// Note that in case of negative delay, we push the name towards the right
|
||||
// so the delay can be shown.
|
||||
// Note that in case of negative delay, it is pushed towards the right so
|
||||
// the delay element does not overlap.
|
||||
createNode({
|
||||
parent: iterations,
|
||||
attributes: {
|
||||
"class": "name",
|
||||
"title": this.getTooltipText(state),
|
||||
// Make space for the negative delay with a margin-left.
|
||||
"style": `margin-left:${negativeDelayW}%`
|
||||
},
|
||||
parent: createNode({
|
||||
parent: this.containerEl,
|
||||
attributes: {
|
||||
"class": "name",
|
||||
"title": this.getTooltipText(state),
|
||||
// Place the name at the same position as the iterations, but make
|
||||
// space for the negative delay if any.
|
||||
"style": `left:${x + negativeDelayW}%;
|
||||
width:${iterationW - negativeDelayW}%;`
|
||||
},
|
||||
}),
|
||||
textContent: state.name
|
||||
});
|
||||
|
||||
@@ -83,10 +89,10 @@ AnimationTimeBlock.prototype = {
|
||||
if (state.delay) {
|
||||
// Negative delays need to start at 0.
|
||||
createNode({
|
||||
parent: iterations,
|
||||
parent: this.containerEl,
|
||||
attributes: {
|
||||
"class": "delay" + (state.delay < 0 ? " negative" : ""),
|
||||
"style": `left:-${delayX}%;
|
||||
"style": `left:${delayX}%;
|
||||
width:${delayW}%;`
|
||||
}
|
||||
});
|
||||
@@ -145,13 +151,24 @@ AnimationTimeBlock.prototype = {
|
||||
|
||||
/**
|
||||
* Get a formatted title for this animation. This will be either:
|
||||
* "some-name", "some-name : CSS Transition", or "some-name : CSS Animation",
|
||||
* depending if the server provides the type, and what type it is.
|
||||
* "some-name", "some-name : CSS Transition", "some-name : CSS Animation",
|
||||
* "some-name : Script Animation", or "Script Animation", depending
|
||||
* if the server provides the type, what type it is and if the animation
|
||||
* has a name
|
||||
* @param {AnimationPlayerFront} animation
|
||||
*/
|
||||
function getFormattedAnimationTitle({state}) {
|
||||
// Older servers don't send the type.
|
||||
return state.type
|
||||
? L10N.getFormatStr("timeline." + state.type + ".nameLabel", state.name)
|
||||
: state.name;
|
||||
// Older servers don't send a type, and only know about
|
||||
// CSSAnimations and CSSTransitions, so it's safe to use
|
||||
// just the name.
|
||||
if (!state.type) {
|
||||
return state.name;
|
||||
}
|
||||
|
||||
// Script-generated animations may not have a name.
|
||||
if (state.type === "scriptanimation" && !state.name) {
|
||||
return L10N.getStr("timeline.scriptanimation.unnamedLabel");
|
||||
}
|
||||
|
||||
return L10N.getFormatStr(`timeline.${state.type}.nameLabel`, state.name);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
@@ -81,7 +87,8 @@ AnimationsTimeline.prototype = {
|
||||
"class": "scrubber-handle"
|
||||
}
|
||||
});
|
||||
this.scrubberHandleEl.addEventListener("mousedown", this.onScrubberMouseDown);
|
||||
this.scrubberHandleEl.addEventListener("mousedown",
|
||||
this.onScrubberMouseDown);
|
||||
|
||||
this.timeHeaderEl = createNode({
|
||||
parent: this.rootWrapperEl,
|
||||
@@ -89,7 +96,8 @@ AnimationsTimeline.prototype = {
|
||||
"class": "time-header track-container"
|
||||
}
|
||||
});
|
||||
this.timeHeaderEl.addEventListener("mousedown", this.onScrubberMouseDown);
|
||||
this.timeHeaderEl.addEventListener("mousedown",
|
||||
this.onScrubberMouseDown);
|
||||
|
||||
this.animationsEl = createNode({
|
||||
parent: this.rootWrapperEl,
|
||||
@@ -99,14 +107,16 @@ AnimationsTimeline.prototype = {
|
||||
}
|
||||
});
|
||||
|
||||
this.win.addEventListener("resize", this.onWindowResize);
|
||||
this.win.addEventListener("resize",
|
||||
this.onWindowResize);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.stopAnimatingScrubber();
|
||||
this.unrender();
|
||||
|
||||
this.win.removeEventListener("resize", this.onWindowResize);
|
||||
this.win.removeEventListener("resize",
|
||||
this.onWindowResize);
|
||||
this.timeHeaderEl.removeEventListener("mousedown",
|
||||
this.onScrubberMouseDown);
|
||||
this.scrubberHandleEl.removeEventListener("mousedown",
|
||||
@@ -144,6 +154,7 @@ AnimationsTimeline.prototype = {
|
||||
for (let animation of this.animations) {
|
||||
animation.off("changed", this.onAnimationStateChanged);
|
||||
}
|
||||
this.stopAnimatingScrubber();
|
||||
TimeScale.reset();
|
||||
this.destroySubComponents("targetNodes");
|
||||
this.destroySubComponents("timeBlocks");
|
||||
@@ -277,9 +288,9 @@ AnimationsTimeline.prototype = {
|
||||
parent: this.animationsEl,
|
||||
nodeType: "li",
|
||||
attributes: {
|
||||
"class": "animation" + (animation.state.isRunningOnCompositor
|
||||
? " fast-track"
|
||||
: "")
|
||||
"class": "animation " +
|
||||
animation.state.type +
|
||||
(animation.state.isRunningOnCompositor ? " fast-track" : "")
|
||||
}
|
||||
});
|
||||
|
||||
@@ -357,16 +368,19 @@ AnimationsTimeline.prototype = {
|
||||
},
|
||||
|
||||
startAnimatingScrubber: function(time) {
|
||||
let x = TimeScale.startTimeToDistance(time);
|
||||
this.scrubberEl.style.left = x + "%";
|
||||
|
||||
// Only stop the scrubber if it's out of bounds or all animations have been
|
||||
// paused, but not if at least an animation is infinite.
|
||||
let isOutOfBounds = time < TimeScale.minStartTime ||
|
||||
time > TimeScale.maxEndTime;
|
||||
let isAllPaused = !this.isAtLeastOneAnimationPlaying();
|
||||
let hasInfinite = this.hasInfiniteAnimations();
|
||||
|
||||
let x = TimeScale.startTimeToDistance(time);
|
||||
if (x > 100 && !hasInfinite) {
|
||||
x = 100;
|
||||
}
|
||||
this.scrubberEl.style.left = x + "%";
|
||||
|
||||
// Only stop the scrubber if it's out of bounds or all animations have been
|
||||
// paused, but not if at least an animation is infinite.
|
||||
if (isAllPaused || (isOutOfBounds && !hasInfinite)) {
|
||||
this.stopAnimatingScrubber();
|
||||
this.emit("timeline-data-changed", {
|
||||
@@ -410,15 +424,21 @@ AnimationsTimeline.prototype = {
|
||||
|
||||
drawHeaderAndBackground: function() {
|
||||
let width = this.timeHeaderEl.offsetWidth;
|
||||
let scale = width / (TimeScale.maxEndTime - TimeScale.minStartTime);
|
||||
let animationDuration = TimeScale.maxEndTime - TimeScale.minStartTime;
|
||||
let minTimeInterval = TIME_GRADUATION_MIN_SPACING *
|
||||
animationDuration / width;
|
||||
let intervalLength = findOptimalTimeInterval(minTimeInterval);
|
||||
let intervalWidth = intervalLength * width / animationDuration;
|
||||
|
||||
drawGraphElementBackground(this.win.document, "time-graduations",
|
||||
width, scale);
|
||||
width, intervalWidth);
|
||||
|
||||
// And the time graduation header.
|
||||
this.timeHeaderEl.innerHTML = "";
|
||||
let interval = findOptimalTimeInterval(scale, TIME_GRADUATION_MIN_SPACING);
|
||||
for (let i = 0; i < width; i += interval) {
|
||||
let pos = 100 * i / width;
|
||||
|
||||
for (let i = 0; i <= width / intervalWidth; i++) {
|
||||
let pos = 100 * i * intervalWidth / width;
|
||||
|
||||
createNode({
|
||||
parent: this.timeHeaderEl,
|
||||
nodeType: "span",
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
@@ -28,7 +34,10 @@ RateSelector.prototype = {
|
||||
this.selectEl = createNode({
|
||||
parent: containerEl,
|
||||
nodeType: "select",
|
||||
attributes: {"class": "devtools-button"}
|
||||
attributes: {
|
||||
"class": "devtools-button",
|
||||
"title": L10N.getStr("timeline.rateSelectorTooltip")
|
||||
}
|
||||
});
|
||||
|
||||
this.selectEl.addEventListener("change", this.onRateChanged);
|
||||
|
||||
@@ -8,6 +8,7 @@ support-files =
|
||||
doc_modify_playbackRate.html
|
||||
doc_negative_animation.html
|
||||
doc_simple_animation.html
|
||||
doc_multiple_animation_types.html
|
||||
head.js
|
||||
|
||||
[browser_animation_animated_properties_displayed.js]
|
||||
@@ -26,6 +27,8 @@ support-files =
|
||||
[browser_animation_running_on_compositor.js]
|
||||
[browser_animation_same_nb_of_playerWidgets_and_playerFronts.js]
|
||||
[browser_animation_shows_player_on_valid_node.js]
|
||||
[browser_animation_spacebar_toggles_animations.js]
|
||||
[browser_animation_spacebar_toggles_node_animations.js]
|
||||
[browser_animation_target_highlight_select.js]
|
||||
[browser_animation_target_highlighter_lock.js]
|
||||
[browser_animation_timeline_currentTime.js]
|
||||
|
||||
+32
-7
@@ -1,19 +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";
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Test that the panel shows no animation data for invalid or not animated nodes
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/animationinspector.properties";
|
||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel, window} = yield openAnimationInspector();
|
||||
let {document} = window;
|
||||
|
||||
let {inspector, panel} = yield openAnimationInspector();
|
||||
yield testEmptyPanel(inspector, panel);
|
||||
});
|
||||
|
||||
function* testEmptyPanel(inspector, panel) {
|
||||
info("Select node .still and check that the panel is empty");
|
||||
let stillNode = yield getNodeFront(".still", inspector);
|
||||
let onUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
@@ -24,6 +25,9 @@ function* testEmptyPanel(inspector, panel) {
|
||||
"No animation players stored in the timeline component for a still node");
|
||||
is(panel.animationsTimelineComponent.animationsEl.childNodes.length, 0,
|
||||
"No animation displayed in the timeline component for a still node");
|
||||
is(document.querySelector("#error-type").textContent,
|
||||
L10N.getStr("panel.invalidElementSelected"),
|
||||
"The correct error message is displayed");
|
||||
|
||||
info("Select the comment text node and check that the panel is empty");
|
||||
let commentNode = yield inspector.walker.previousSibling(stillNode);
|
||||
@@ -34,4 +38,25 @@ function* testEmptyPanel(inspector, panel) {
|
||||
is(panel.animationsTimelineComponent.animations.length, 0,
|
||||
"No animation players stored in the timeline component for a text node");
|
||||
is(panel.animationsTimelineComponent.animationsEl.childNodes.length, 0,
|
||||
"No animation displayed in the timeline component for a text node");}
|
||||
"No animation displayed in the timeline component for a text node");
|
||||
is(document.querySelector("#error-type").textContent,
|
||||
L10N.getStr("panel.invalidElementSelected"),
|
||||
"The correct error message is displayed");
|
||||
|
||||
info("Select the pseudo element node and check that the panel is empty " +
|
||||
"and contains the special animated pseudo-element message");
|
||||
let pseudoElParent = yield getNodeFront(".pseudo", inspector);
|
||||
let {nodes} = yield inspector.walker.children(pseudoElParent);
|
||||
let pseudoEl = nodes[0];
|
||||
onUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
yield selectNode(pseudoEl, inspector);
|
||||
yield onUpdated;
|
||||
|
||||
is(panel.animationsTimelineComponent.animations.length, 0,
|
||||
"No animation players stored in the timeline component for a pseudo-node");
|
||||
is(panel.animationsTimelineComponent.animationsEl.childNodes.length, 0,
|
||||
"No animation displayed in the timeline component for a pseudo-node");
|
||||
is(document.querySelector("#error-type").textContent,
|
||||
L10N.getStr("panel.pseudoElementSelected"),
|
||||
"The correct error message is displayed");
|
||||
});
|
||||
|
||||
@@ -10,10 +10,14 @@ add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8,welcome to the animation panel");
|
||||
let {panel, controller} = yield openAnimationInspector();
|
||||
|
||||
ok(controller, "The animation controller exists");
|
||||
ok(controller.animationsFront, "The animation controller has been initialized");
|
||||
|
||||
ok(panel, "The animation panel exists");
|
||||
ok(panel.playersEl, "The animation panel has been initialized");
|
||||
ok(panel.animationsTimelineComponent, "The animation panel has been initialized");
|
||||
ok(controller,
|
||||
"The animation controller exists");
|
||||
ok(controller.animationsFront,
|
||||
"The animation controller has been initialized");
|
||||
ok(panel,
|
||||
"The animation panel exists");
|
||||
ok(panel.playersEl,
|
||||
"The animation panel has been initialized");
|
||||
ok(panel.animationsTimelineComponent,
|
||||
"The animation panel has been initialized");
|
||||
});
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@ add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel, controller} = yield openAnimationInspector();
|
||||
|
||||
info("Listen for the players-updated, ui-updated and inspector-updated events");
|
||||
info("Listen for the players-updated, ui-updated and " +
|
||||
"inspector-updated events");
|
||||
let receivedEvents = [];
|
||||
controller.once(controller.PLAYERS_UPDATED_EVENT, () => {
|
||||
receivedEvents.push(controller.PLAYERS_UPDATED_EVENT);
|
||||
|
||||
-15
@@ -26,24 +26,9 @@ add_task(function*() {
|
||||
is(controller.animationPlayers.length, 2,
|
||||
"2 AnimationPlayerFronts have been created");
|
||||
|
||||
// Hold on to one of the AnimationPlayerFront objects and mock its release
|
||||
// method to test that it is released correctly and that its auto-refresh is
|
||||
// stopped.
|
||||
let retainedFront = controller.animationPlayers[0];
|
||||
let oldRelease = retainedFront.release;
|
||||
let releaseCalled = false;
|
||||
retainedFront.release = () => {
|
||||
releaseCalled = true;
|
||||
};
|
||||
|
||||
info("Selecting a node with no animations");
|
||||
yield selectNode(".still", inspector);
|
||||
|
||||
is(controller.animationPlayers.length, 0,
|
||||
"There are no more AnimationPlayerFront objects");
|
||||
|
||||
info("Checking the destroyed AnimationPlayerFront object");
|
||||
ok(releaseCalled, "The AnimationPlayerFront has been released");
|
||||
|
||||
yield oldRelease.call(retainedFront);
|
||||
});
|
||||
|
||||
+34
-4
@@ -7,11 +7,41 @@
|
||||
// Test that player widgets are displayed right when the animation panel is
|
||||
// initialized, if the selected node (<body> by default) is animated.
|
||||
|
||||
const { ANIMATION_TYPES } = require("devtools/server/actors/animation");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_body_animation.html");
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.animations-api.core.enabled", true]
|
||||
]}, resolve);
|
||||
});
|
||||
|
||||
yield addTab(TEST_URL_ROOT + "doc_multiple_animation_types.html");
|
||||
|
||||
let {panel} = yield openAnimationInspector();
|
||||
is(panel.animationsTimelineComponent.animations.length, 1,
|
||||
"One animation is handled by the timeline after init");
|
||||
assertAnimationsDisplayed(panel, 1, "One animation is displayed after init");
|
||||
is(panel.animationsTimelineComponent.animations.length, 3,
|
||||
"Three animations are handled by the timeline after init");
|
||||
assertAnimationsDisplayed(panel, 3,
|
||||
"Three animations are displayed after init");
|
||||
is(
|
||||
panel.animationsTimelineComponent
|
||||
.animationsEl
|
||||
.querySelectorAll(`.animation.${ANIMATION_TYPES.SCRIPT_ANIMATION}`)
|
||||
.length,
|
||||
1,
|
||||
"One script-generated animation is displayed");
|
||||
is(
|
||||
panel.animationsTimelineComponent
|
||||
.animationsEl
|
||||
.querySelectorAll(`.animation.${ANIMATION_TYPES.CSS_ANIMATION}`)
|
||||
.length,
|
||||
1,
|
||||
"One CSS animation is displayed");
|
||||
is(
|
||||
panel.animationsTimelineComponent
|
||||
.animationsEl
|
||||
.querySelectorAll(`.animation.${ANIMATION_TYPES.CSS_TRANSITION}`)
|
||||
.length,
|
||||
1,
|
||||
"One CSS transition is displayed");
|
||||
});
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/* 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";
|
||||
|
||||
// Test that the spacebar key press toggles the toggleAll button state
|
||||
// when a node with no animation is selected.
|
||||
// This test doesn't need to test if animations actually pause/resume
|
||||
// because there's an other test that does this :
|
||||
// browser_animation_toggle_button_toggles_animation.js
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {panel, inspector, window, controller} = yield openAnimationInspector();
|
||||
let {toggleAllButtonEl} = panel;
|
||||
|
||||
// select a node without animations
|
||||
yield selectNode(".still", inspector);
|
||||
|
||||
// ensure the focus is on the animation panel
|
||||
window.focus();
|
||||
|
||||
info("Simulate spacebar stroke and check toggleAll button" +
|
||||
" is in paused state");
|
||||
|
||||
// sending the key will lead to a ALL_ANIMATIONS_TOGGLED_EVENT
|
||||
let onToggled = once(controller, controller.ALL_ANIMATIONS_TOGGLED_EVENT);
|
||||
EventUtils.sendKey("SPACE", window);
|
||||
yield onToggled;
|
||||
ok(toggleAllButtonEl.classList.contains("paused"),
|
||||
"The toggle all button is in its paused state");
|
||||
|
||||
info("Simulate spacebar stroke and check toggleAll button" +
|
||||
" is in playing state");
|
||||
|
||||
// sending the key will lead to a ALL_ANIMATIONS_TOGGLED_EVENT
|
||||
onToggled = once(controller, controller.ALL_ANIMATIONS_TOGGLED_EVENT);
|
||||
EventUtils.sendKey("SPACE", window);
|
||||
yield onToggled;
|
||||
ok(!toggleAllButtonEl.classList.contains("paused"),
|
||||
"The toggle all button is in its playing state again");
|
||||
});
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/* 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";
|
||||
|
||||
// Test that the spacebar key press toggles the play/resume button state.
|
||||
// This test doesn't need to test if animations actually pause/resume
|
||||
// because there's an other test that does this.
|
||||
// There are animations in the test page and since, by default, the <body> node
|
||||
// is selected, animations will be displayed in the timeline, so the timeline
|
||||
// play/resume button will be displayed
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {panel, window} = yield openAnimationInspector();
|
||||
let {playTimelineButtonEl} = panel;
|
||||
|
||||
// ensure the focus is on the animation panel
|
||||
window.focus();
|
||||
|
||||
info("Simulate spacebar stroke and check playResume button" +
|
||||
" is in paused state");
|
||||
|
||||
// sending the key will lead to a UI_UPDATE_EVENT
|
||||
let onUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
EventUtils.sendKey("SPACE", window);
|
||||
yield onUpdated;
|
||||
ok(playTimelineButtonEl.classList.contains("paused"),
|
||||
"The play/resume button is in its paused state");
|
||||
|
||||
info("Simulate spacebar stroke and check playResume button" +
|
||||
" is in playing state");
|
||||
|
||||
// sending the key will lead to a UI_UPDATE_EVENT
|
||||
onUpdated = panel.once(panel.UI_UPDATED_EVENT);
|
||||
EventUtils.sendKey("SPACE", window);
|
||||
yield onUpdated;
|
||||
ok(!playTimelineButtonEl.classList.contains("paused"),
|
||||
"The play/resume button is in its play state again");
|
||||
});
|
||||
@@ -6,11 +6,10 @@
|
||||
|
||||
// Check that the timeline shows correct time graduations in the header.
|
||||
|
||||
const {
|
||||
findOptimalTimeInterval,
|
||||
TimeScale
|
||||
} = require("devtools/client/animationinspector/utils");
|
||||
// Should be kept in sync with TIME_GRADUATION_MIN_SPACING in animation-timeline.js
|
||||
const {findOptimalTimeInterval, TimeScale} = require("devtools/client/animationinspector/utils");
|
||||
|
||||
// Should be kept in sync with TIME_GRADUATION_MIN_SPACING in
|
||||
// animation-timeline.js
|
||||
const TIME_GRADUATION_MIN_SPACING = 40;
|
||||
|
||||
add_task(function*() {
|
||||
@@ -22,11 +21,14 @@ add_task(function*() {
|
||||
|
||||
info("Find out how many time graduations should there be");
|
||||
let width = headerEl.offsetWidth;
|
||||
let scale = width / (TimeScale.maxEndTime - TimeScale.minStartTime);
|
||||
|
||||
let animationDuration = TimeScale.maxEndTime - TimeScale.minStartTime;
|
||||
let minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
|
||||
|
||||
// Note that findOptimalTimeInterval is tested separately in xpcshell test
|
||||
// test_findOptimalTimeInterval.js, so we assume that it works here.
|
||||
let interval = findOptimalTimeInterval(scale, TIME_GRADUATION_MIN_SPACING);
|
||||
let nb = Math.ceil(width / interval);
|
||||
let interval = findOptimalTimeInterval(minTimeInterval);
|
||||
let nb = Math.ceil(animationDuration / interval);
|
||||
|
||||
is(headerEl.querySelectorAll(".time-tick").length, nb,
|
||||
"The expected number of time ticks were found");
|
||||
@@ -34,9 +36,9 @@ add_task(function*() {
|
||||
info("Make sure graduations are evenly distributed and show the right times");
|
||||
[...headerEl.querySelectorAll(".time-tick")].forEach((tick, i) => {
|
||||
let left = parseFloat(tick.style.left);
|
||||
let expectedPos = i * interval * 100 / width;
|
||||
let expectedPos = i * interval * 100 / animationDuration;
|
||||
is(Math.round(left), Math.round(expectedPos),
|
||||
"Graduation " + i + " is positioned correctly");
|
||||
`Graduation ${i} is positioned correctly`);
|
||||
|
||||
// Note that the distancetoRelativeTime and formatTime functions are tested
|
||||
// separately in xpcshell test test_timeScale.js, so we assume that they
|
||||
@@ -44,6 +46,6 @@ add_task(function*() {
|
||||
let formattedTime = TimeScale.formatTime(
|
||||
TimeScale.distanceToRelativeTime(expectedPos, width));
|
||||
is(tick.textContent, formattedTime,
|
||||
"Graduation " + i + " has the right text content");
|
||||
`Graduation ${i} has the right text content`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,11 +95,12 @@ function waitForOutOfBoundScrubber({win, scrubberEl}) {
|
||||
|
||||
function waitForScrubberStopped(timeline) {
|
||||
return new Promise(resolve => {
|
||||
timeline.on("timeline-data-changed", function onTimelineData(e, {isMoving}) {
|
||||
if (!isMoving) {
|
||||
timeline.off("timeline-data-changed", onTimelineData);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
timeline.on("timeline-data-changed",
|
||||
function onTimelineData(e, {isMoving}) {
|
||||
if (!isMoving) {
|
||||
timeline.off("timeline-data-changed", onTimelineData);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
|
||||
let {panel, controller} = yield openAnimationInspector();
|
||||
let players = controller.animationPlayers;
|
||||
let btn = panel.rewindTimelineButtonEl;
|
||||
|
||||
ok(btn, "The rewind button exists");
|
||||
@@ -24,9 +25,9 @@ add_task(function*() {
|
||||
info("Check that the scrubber has stopped moving");
|
||||
yield assertScrubberMoving(panel, false);
|
||||
|
||||
ok(controller.animationPlayers.every(({state}) => state.currentTime === 0),
|
||||
ok(players.every(({state}) => state.currentTime === 0),
|
||||
"All animations' currentTimes have been set to 0");
|
||||
ok(controller.animationPlayers.every(({state}) => state.playState === "paused"),
|
||||
ok(players.every(({state}) => state.playState === "paused"),
|
||||
"All animations have been paused");
|
||||
|
||||
info("Play the animations again");
|
||||
@@ -41,8 +42,8 @@ add_task(function*() {
|
||||
info("Check that the scrubber has stopped moving");
|
||||
yield assertScrubberMoving(panel, false);
|
||||
|
||||
ok(controller.animationPlayers.every(({state}) => state.currentTime === 0),
|
||||
ok(players.every(({state}) => state.currentTime === 0),
|
||||
"All animations' currentTimes have been set to 0");
|
||||
ok(controller.animationPlayers.every(({state}) => state.playState === "paused"),
|
||||
ok(players.every(({state}) => state.playState === "paused"),
|
||||
"All animations have been paused");
|
||||
});
|
||||
|
||||
@@ -39,14 +39,15 @@ function checkDelayAndName(timelineEl, hasDelay) {
|
||||
let targetNode = timelineEl.querySelector(".target");
|
||||
|
||||
// Check that the delay element does not cause the timeline to overflow.
|
||||
let delayRect = delay.getBoundingClientRect();
|
||||
let sidebarWidth = targetNode.getBoundingClientRect().width;
|
||||
ok(delayRect.x >= sidebarWidth,
|
||||
let delayLeft = Math.round(delay.getBoundingClientRect().x);
|
||||
let sidebarWidth = Math.round(targetNode.getBoundingClientRect().width);
|
||||
ok(delayLeft >= sidebarWidth,
|
||||
"The delay element isn't displayed over the sidebar");
|
||||
|
||||
// Check that the delay is not displayed on top of the name.
|
||||
let nameLeft = name.getBoundingClientRect().left;
|
||||
ok(delayRect.right <= nameLeft,
|
||||
let delayRight = Math.round(delay.getBoundingClientRect().right);
|
||||
let nameLeft = Math.round(name.getBoundingClientRect().left);
|
||||
ok(delayRight <= nameLeft,
|
||||
"The delay element does not span over the name element");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* globals addMessageListener, sendAsyncMessage */
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -11,7 +12,8 @@
|
||||
* @param {Object} data
|
||||
* - {String} selector The CSS selector to get the node (can be a "super"
|
||||
* selector).
|
||||
* - {Number} animationIndex The index of the node's animationPlayers to play or pause
|
||||
* - {Number} animationIndex The index of the node's animationPlayers to play
|
||||
* or pause
|
||||
* - {Boolean} pause True to pause the animation, false to play.
|
||||
*/
|
||||
addMessageListener("Test:ToggleAnimationPlayer", function(msg) {
|
||||
@@ -103,18 +105,18 @@ addMessageListener("Test:GetAnimationPlayerState", function(msg) {
|
||||
* @param {String} superSelector.
|
||||
* @return {DOMNode} The node, or null if not found.
|
||||
*/
|
||||
function superQuerySelector(superSelector, root=content.document) {
|
||||
function superQuerySelector(superSelector, root = content.document) {
|
||||
let frameIndex = superSelector.indexOf("||");
|
||||
if (frameIndex === -1) {
|
||||
return root.querySelector(superSelector);
|
||||
} else {
|
||||
let rootSelector = superSelector.substring(0, frameIndex).trim();
|
||||
let childSelector = superSelector.substring(frameIndex+2).trim();
|
||||
root = root.querySelector(rootSelector);
|
||||
if (!root || !root.contentWindow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return superQuerySelector(childSelector, root.contentWindow.document);
|
||||
}
|
||||
|
||||
let rootSelector = superSelector.substring(0, frameIndex).trim();
|
||||
let childSelector = superSelector.substring(frameIndex + 2).trim();
|
||||
root = root.querySelector(rootSelector);
|
||||
if (!root || !root.contentWindow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return superQuerySelector(childSelector, root.contentWindow.document);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
<div></div>
|
||||
<div class="rate"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var el = document.querySelector(".rate");
|
||||
var ani = el.getAnimations()[0];
|
||||
ani.playbackRate = 2;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
.ball {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.script-animation {
|
||||
background: #f06;
|
||||
}
|
||||
|
||||
.css-transition {
|
||||
background: #006;
|
||||
transition: background-color 20s;
|
||||
}
|
||||
|
||||
.css-animation {
|
||||
background: #a06;
|
||||
animation: flash 10s forwards;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ball script-animation"></div>
|
||||
<div class="ball css-animation"></div>
|
||||
<div class="ball css-transition"></div>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
setTimeout(function() {
|
||||
document.querySelector(".css-transition").style.backgroundColor = "yellow";
|
||||
}, 0);
|
||||
|
||||
document.querySelector(".script-animation").animate([
|
||||
{opacity: 1, offset: 0},
|
||||
{opacity: .1, offset: 1}
|
||||
], {
|
||||
duration: 10000,
|
||||
fill: "forwards"
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -45,6 +45,8 @@
|
||||
<div class="zero"></div>
|
||||
<div class="positive"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var negative = document.querySelector(".negative");
|
||||
var zero = document.querySelector(".zero");
|
||||
var positive = document.querySelector(".positive");
|
||||
|
||||
@@ -82,6 +82,21 @@
|
||||
animation: no-compositor 10s cubic-bezier(.57,-0.02,1,.31) forwards;
|
||||
}
|
||||
|
||||
.pseudo {
|
||||
top: 800px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.pseudo::before {
|
||||
content: "";
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
border-radius: 50%;
|
||||
background: black;
|
||||
position: absolute;
|
||||
animation: simple-animation 1s infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes simple-animation {
|
||||
100% {
|
||||
transform: translateX(300px);
|
||||
@@ -112,5 +127,6 @@
|
||||
<div class="ball long"></div>
|
||||
<div class="ball negative-delay"></div>
|
||||
<div class="ball no-compositor"></div>
|
||||
<div class="ball pseudo"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -114,7 +115,7 @@ function getNodeFront(selector, {walker}) {
|
||||
* to highlight the node upon selection
|
||||
* @return {Promise} Resolves when the inspector is updated with the new node
|
||||
*/
|
||||
var selectNode = Task.async(function*(data, inspector, reason="test") {
|
||||
var selectNode = Task.async(function*(data, inspector, reason = "test") {
|
||||
info("Selecting the node for '" + data + "'");
|
||||
let nodeFront = data;
|
||||
if (!data._form) {
|
||||
@@ -258,7 +259,7 @@ function hasSideBarTab(inspector, id) {
|
||||
* @param {Boolean} useCapture Optional, for add/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function once(target, eventName, useCapture=false) {
|
||||
function once(target, eventName, useCapture = false) {
|
||||
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
||||
|
||||
let deferred = promise.defer();
|
||||
@@ -312,7 +313,8 @@ function waitForContentMessage(name) {
|
||||
* @return {Promise} Resolves to the response data if a response is expected,
|
||||
* immediately resolves otherwise
|
||||
*/
|
||||
function executeInContent(name, data={}, objects={}, expectResponse=true) {
|
||||
function executeInContent(name, data = {}, objects = {},
|
||||
expectResponse = true) {
|
||||
info("Sending message " + name + " to content");
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
|
||||
@@ -327,7 +329,8 @@ function executeInContent(name, data={}, objects={}, expectResponse=true) {
|
||||
/**
|
||||
* Get the current playState of an animation player on a given node.
|
||||
*/
|
||||
var getAnimationPlayerState = Task.async(function*(selector, animationIndex=0) {
|
||||
var getAnimationPlayerState = Task.async(function*(selector,
|
||||
animationIndex = 0) {
|
||||
let playState = yield executeInContent("Test:GetAnimationPlayerState",
|
||||
{selector, animationIndex});
|
||||
return playState;
|
||||
@@ -366,7 +369,6 @@ var waitForAllAnimationTargets = Task.async(function*(panel) {
|
||||
*/
|
||||
function* assertScrubberMoving(panel, isMoving) {
|
||||
let timeline = panel.animationsTimelineComponent;
|
||||
let scrubberEl = timeline.scrubberEl;
|
||||
|
||||
if (isMoving) {
|
||||
// If we expect the scrubber to move, just wait for a couple of
|
||||
@@ -446,8 +448,8 @@ function* changeTimelinePlaybackRate(panel, rate) {
|
||||
|
||||
// Simulate a mousemove outside of the rate selector area to avoid subsequent
|
||||
// tests from failing because of unwanted mouseover events.
|
||||
EventUtils.synthesizeMouseAtCenter(win.document.querySelector("#timeline-toolbar"),
|
||||
{type: "mousemove"}, win);
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
win.document.querySelector("#timeline-toolbar"), {type: "mousemove"}, win);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,6 +521,8 @@ function getKeyframeComponent(panel, animationIndex, propertyName) {
|
||||
* @return {DOMNode} The keyframe element.
|
||||
*/
|
||||
function getKeyframeEl(panel, animationIndex, propertyName, keyframeIndex) {
|
||||
let keyframeComponent = getKeyframeComponent(panel, animationIndex, propertyName);
|
||||
return keyframeComponent.keyframesEl.querySelectorAll(".frame")[keyframeIndex];
|
||||
let keyframeComponent = getKeyframeComponent(panel, animationIndex,
|
||||
propertyName);
|
||||
return keyframeComponent.keyframesEl
|
||||
.querySelectorAll(".frame")[keyframeIndex];
|
||||
}
|
||||
|
||||
@@ -14,66 +14,64 @@ const {findOptimalTimeInterval} = require("devtools/client/animationinspector/ut
|
||||
// findOptimalTimeInterval function. Each object should have the following
|
||||
// properties:
|
||||
// - desc: an optional string that will be printed out
|
||||
// - timeScale: a number that represents how many pixels is 1ms
|
||||
// - minSpacing: an optional number that represents the minim space between 2
|
||||
// time graduations
|
||||
// - minTimeInterval: a number that represents the minimum time in ms
|
||||
// that should be displayed in one interval
|
||||
// - expectedInterval: a number that you expect the findOptimalTimeInterval
|
||||
// function to return as a result.
|
||||
// Optionally you can pass a string where `interval` is the calculated
|
||||
// interval, this string will be eval'd and tested to be truthy.
|
||||
const TEST_DATA = [{
|
||||
desc: "With 1px being 1ms and no minSpacing, expect the interval to be the " +
|
||||
"interval multiple",
|
||||
timeScale: 1,
|
||||
minSpacing: undefined,
|
||||
desc: "With no minTimeInterval, expect the interval to be 0",
|
||||
minTimeInterval: null,
|
||||
expectedInterval: 0
|
||||
}, {
|
||||
desc: "With a minTimeInterval of 0 ms, expect the interval to be 0",
|
||||
minTimeInterval: 0,
|
||||
expectedInterval: 0
|
||||
}, {
|
||||
desc: "With a minInterval of 1ms, expect the interval to be the 1ms too",
|
||||
minTimeInterval: 1,
|
||||
expectedInterval: 1
|
||||
}, {
|
||||
desc: "With a very small minTimeInterval, expect the interval to be 1ms",
|
||||
minTimeInterval: 1e-31,
|
||||
expectedInterval: 1
|
||||
}, {
|
||||
desc: "With a minInterval of 2.5ms, expect the interval to be 2.5ms too",
|
||||
minTimeInterval: 2.5,
|
||||
expectedInterval: 2.5
|
||||
}, {
|
||||
desc: "With a minInterval of 5ms, expect the interval to be 5ms too",
|
||||
minTimeInterval: 5,
|
||||
expectedInterval: 5
|
||||
}, {
|
||||
desc: "With a minInterval of 7ms, expect the interval to be the next " +
|
||||
"multiple of 5",
|
||||
minTimeInterval: 7,
|
||||
expectedInterval: 10
|
||||
}, {
|
||||
minTimeInterval: 20,
|
||||
expectedInterval: 25
|
||||
}, {
|
||||
desc: "With 1px being 1ms and a custom minSpacing being a multiple of 25 " +
|
||||
"expect the interval to be the custom min spacing",
|
||||
timeScale: 1,
|
||||
minSpacing: 50,
|
||||
minTimeInterval: 33,
|
||||
expectedInterval: 50
|
||||
}, {
|
||||
desc: "With 1px being 1ms and a custom minSpacing not being multiple of 25 " +
|
||||
"expect the interval to be the next multiple of 10",
|
||||
timeScale: 1,
|
||||
minSpacing: 26,
|
||||
expectedInterval: 50
|
||||
minTimeInterval: 987,
|
||||
expectedInterval: 1000
|
||||
}, {
|
||||
desc: "If 1ms corresponds to a distance that is greater than the min " +
|
||||
"spacing then, expect the interval to be this distance",
|
||||
timeScale: 20,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: 20
|
||||
minTimeInterval: 1234,
|
||||
expectedInterval: 2500
|
||||
}, {
|
||||
desc: "If 1ms corresponds to a distance that is greater than the min " +
|
||||
"spacing then, expect the interval to be this distance, even if it " +
|
||||
"isn't a multiple of 25",
|
||||
timeScale: 33,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: 33
|
||||
}, {
|
||||
desc: "If 1ms is a very small distance, then expect this distance to be " +
|
||||
"multiplied by 25, 50, 100, 200, etc... until it goes over the min " +
|
||||
"spacing",
|
||||
timeScale: 0.001,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: 12.8
|
||||
}, {
|
||||
desc: "If the time scale is such that we need to iterate more than the " +
|
||||
"maximum allowed number of iterations, then expect an interval lower " +
|
||||
"than the minimum one",
|
||||
timeScale: 1e-31,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: "interval < 10"
|
||||
minTimeInterval: 9800,
|
||||
expectedInterval: 10000
|
||||
}];
|
||||
|
||||
function run_test() {
|
||||
for (let {timeScale, desc, minSpacing, expectedInterval} of TEST_DATA) {
|
||||
do_print("Testing timeScale: " + timeScale + " and minSpacing: " +
|
||||
minSpacing + ". Expecting " + expectedInterval + ".");
|
||||
for (let {minTimeInterval, desc, expectedInterval} of TEST_DATA) {
|
||||
do_print(`Testing minTimeInterval: ${minTimeInterval}.
|
||||
Expecting ${expectedInterval}.`);
|
||||
|
||||
let interval = findOptimalTimeInterval(timeScale, minSpacing);
|
||||
let interval = findOptimalTimeInterval(minTimeInterval);
|
||||
if (typeof expectedInterval == "string") {
|
||||
ok(eval(expectedInterval), desc);
|
||||
} else {
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
"use strict";
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {formatStopwatchTime} = require("devtools/client/animationinspector/utils");
|
||||
|
||||
|
||||
const TEST_DATA = [{
|
||||
desc: "Formatting 0",
|
||||
time: 0,
|
||||
|
||||
@@ -8,27 +8,23 @@
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
var {loader} = Cu.import("resource://devtools/shared/Loader.jsm");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/shared/event-emitter");
|
||||
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/animationinspector.properties";
|
||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
// How many times, maximum, can we loop before we find the optimal time
|
||||
// interval in the timeline graph.
|
||||
const OPTIMAL_TIME_INTERVAL_MAX_ITERS = 100;
|
||||
// Background time graduations should be multiple of this number of millis.
|
||||
const TIME_INTERVAL_MULTIPLE = 25;
|
||||
const TIME_INTERVAL_SCALES = 3;
|
||||
// The default minimum spacing between time graduations in px.
|
||||
const TIME_GRADUATION_MIN_SPACING = 10;
|
||||
// Time graduations should be multiple of one of these number.
|
||||
const OPTIMAL_TIME_INTERVAL_MULTIPLES = [1, 2.5, 5];
|
||||
|
||||
// RGB color for the time interval background.
|
||||
const TIME_INTERVAL_COLOR = [128, 136, 144];
|
||||
// byte
|
||||
const TIME_INTERVAL_OPACITY_MIN = 32;
|
||||
const TIME_INTERVAL_OPACITY_MIN = 64;
|
||||
// byte
|
||||
const TIME_INTERVAL_OPACITY_ADD = 32;
|
||||
const TIME_INTERVAL_OPACITY_MAX = 96;
|
||||
|
||||
const MILLIS_TIME_FORMAT_MAX_DURATION = 4000;
|
||||
|
||||
@@ -69,12 +65,13 @@ exports.createNode = createNode;
|
||||
* Given a data-scale, draw the background for a graph (vertical lines) into a
|
||||
* canvas and set that canvas as an image-element with an ID that can be used
|
||||
* from CSS.
|
||||
* @param {Document} document The document where the image-element should be set.
|
||||
* @param {Document} document The document where the image-element should be
|
||||
* set.
|
||||
* @param {String} id The ID for the image-element.
|
||||
* @param {Number} graphWidth The width of the graph.
|
||||
* @param {Number} timeScale How many px is 1ms in the graph.
|
||||
* @param {Number} intervalWidth The width of one interval
|
||||
*/
|
||||
function drawGraphElementBackground(document, id, graphWidth, timeScale) {
|
||||
function drawGraphElementBackground(document, id, graphWidth, intervalWidth) {
|
||||
let canvas = document.createElement("canvas");
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
@@ -93,17 +90,19 @@ function drawGraphElementBackground(document, id, graphWidth, timeScale) {
|
||||
|
||||
// Build new millisecond tick lines...
|
||||
let [r, g, b] = TIME_INTERVAL_COLOR;
|
||||
let alphaComponent = TIME_INTERVAL_OPACITY_MIN;
|
||||
let interval = findOptimalTimeInterval(timeScale);
|
||||
let opacities = [TIME_INTERVAL_OPACITY_MAX, TIME_INTERVAL_OPACITY_MIN];
|
||||
|
||||
// Insert one pixel for each division on each scale.
|
||||
for (let i = 1; i <= TIME_INTERVAL_SCALES; i++) {
|
||||
let increment = interval * Math.pow(2, i);
|
||||
for (let x = 0; x < canvas.width; x += increment) {
|
||||
let position = x | 0;
|
||||
view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
||||
// Insert one tick line on each interval
|
||||
for (let i = 0; i <= graphWidth / intervalWidth; i++) {
|
||||
let x = i * intervalWidth;
|
||||
// Ensure the last line is drawn on canvas
|
||||
if (x >= graphWidth) {
|
||||
x = graphWidth - 0.5;
|
||||
}
|
||||
alphaComponent += TIME_INTERVAL_OPACITY_ADD;
|
||||
let position = x | 0;
|
||||
let alphaComponent = opacities[i % opacities.length];
|
||||
|
||||
view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
// Flush the image data and cache the waterfall background.
|
||||
@@ -116,31 +115,30 @@ exports.drawGraphElementBackground = drawGraphElementBackground;
|
||||
|
||||
/**
|
||||
* Find the optimal interval between time graduations in the animation timeline
|
||||
* graph based on a time scale and a minimum spacing.
|
||||
* @param {Number} timeScale How many px is 1ms in the graph.
|
||||
* @param {Number} minSpacing The minimum spacing between 2 graduations,
|
||||
* defaults to TIME_GRADUATION_MIN_SPACING.
|
||||
* @return {Number} The optimal interval, in pixels.
|
||||
* graph based on a minimum time interval
|
||||
* @param {Number} minTimeInterval Minimum time in ms in one interval
|
||||
* @return {Number} The optimal interval time in ms
|
||||
*/
|
||||
function findOptimalTimeInterval(timeScale,
|
||||
minSpacing=TIME_GRADUATION_MIN_SPACING) {
|
||||
let timingStep = TIME_INTERVAL_MULTIPLE;
|
||||
function findOptimalTimeInterval(minTimeInterval) {
|
||||
let numIters = 0;
|
||||
let multiplier = 1;
|
||||
|
||||
if (timeScale > minSpacing) {
|
||||
return timeScale;
|
||||
if (!minTimeInterval) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let interval;
|
||||
while (true) {
|
||||
let scaledStep = timeScale * timingStep;
|
||||
for (let i = 0; i < OPTIMAL_TIME_INTERVAL_MULTIPLES.length; i++) {
|
||||
interval = OPTIMAL_TIME_INTERVAL_MULTIPLES[i] * multiplier;
|
||||
if (minTimeInterval <= interval) {
|
||||
return interval;
|
||||
}
|
||||
}
|
||||
if (++numIters > OPTIMAL_TIME_INTERVAL_MAX_ITERS) {
|
||||
return scaledStep;
|
||||
return interval;
|
||||
}
|
||||
if (scaledStep < minSpacing) {
|
||||
timingStep *= 2;
|
||||
continue;
|
||||
}
|
||||
return scaledStep;
|
||||
multiplier *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +161,10 @@ function formatStopwatchTime(time) {
|
||||
|
||||
let pad = (nb, max) => {
|
||||
if (nb < max) {
|
||||
return new Array((max+"").length - (nb+"").length + 1).join("0") + nb;
|
||||
return new Array((max + "").length - (nb + "").length + 1).join("0") + nb;
|
||||
}
|
||||
return nb;
|
||||
}
|
||||
};
|
||||
|
||||
minutes = pad(minutes, 10);
|
||||
seconds = pad(seconds, 10);
|
||||
@@ -301,10 +299,10 @@ var TimeScale = {
|
||||
// The width for all iterations.
|
||||
let iterationW = w * (count || 1);
|
||||
// The start position of the delay.
|
||||
let delayX = this.durationToDistance((delay < 0 ? 0 : delay) / rate);
|
||||
let delayX = delay < 0 ? x : this.startTimeToDistance(start);
|
||||
// The width of the delay.
|
||||
let delayW = this.durationToDistance(Math.abs(delay) / rate);
|
||||
// The width of the delay if it is positive, 0 otherwise.
|
||||
// The width of the delay if it is negative, 0 otherwise.
|
||||
let negativeDelayW = delay < 0 ? delayW : 0;
|
||||
|
||||
return {x, w, iterationW, delayX, delayW, negativeDelayW};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// Test various GCLI commands
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,gcli-commands";
|
||||
const HUDService = require("devtools/client/webconsole/hudservice");
|
||||
|
||||
function test() {
|
||||
return Task.spawn(spawnTest).then(finish, helpers.handleError);
|
||||
|
||||
@@ -101,6 +101,7 @@ function update(state = initialState, action, emitChange) {
|
||||
case constants.SET_BREAKPOINT_CONDITION: {
|
||||
const id = makeLocationId(action.breakpoint.location);
|
||||
const bp = state.breakpoints[id];
|
||||
emitChange("breakpoint-condition-updated", bp);
|
||||
|
||||
if (action.status === 'start') {
|
||||
return mergeIn(state, ['breakpoints', id], {
|
||||
@@ -117,8 +118,10 @@ function update(state = initialState, action, emitChange) {
|
||||
});
|
||||
}
|
||||
else if (action.status === 'error') {
|
||||
emitChange("breakpoint-removed", bp);
|
||||
return deleteIn(state, ['breakpoints', id]);
|
||||
}
|
||||
|
||||
break;
|
||||
}}
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ var DebuggerView = {
|
||||
"breakpoint-enabled": this.addEditorBreakpoint,
|
||||
"breakpoint-disabled": this.removeEditorBreakpoint,
|
||||
"breakpoint-removed": this.removeEditorBreakpoint,
|
||||
"breakpoint-condition-updated": this.renderEditorBreakpointCondition,
|
||||
"breakpoint-moved": ({ breakpoint, prevLocation }) => {
|
||||
const selectedSource = queries.getSelectedSource(getState());
|
||||
const { location } = breakpoint;
|
||||
@@ -351,13 +352,13 @@ var DebuggerView = {
|
||||
},
|
||||
|
||||
addEditorBreakpoint: function(breakpoint) {
|
||||
const { location } = breakpoint;
|
||||
const { location, condition } = breakpoint;
|
||||
const source = queries.getSelectedSource(this.controller.getState());
|
||||
|
||||
if (source &&
|
||||
source.actor === location.actor &&
|
||||
!breakpoint.disabled) {
|
||||
this.editor.addBreakpoint(location.line - 1);
|
||||
this.editor.addBreakpoint(location.line - 1, condition);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -370,6 +371,19 @@ var DebuggerView = {
|
||||
}
|
||||
},
|
||||
|
||||
renderEditorBreakpointCondition: function (breakpoint) {
|
||||
const { location, condition } = breakpoint;
|
||||
const source = queries.getSelectedSource(this.controller.getState());
|
||||
|
||||
if (source && source.actor === location.actor) {
|
||||
if (condition) {
|
||||
this.editor.setBreakpointCondition(location.line - 1);
|
||||
} else {
|
||||
this.editor.removeBreakpointCondition(location.line - 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the source editor.
|
||||
*/
|
||||
|
||||
@@ -102,8 +102,8 @@ Tools.inspector = {
|
||||
],
|
||||
|
||||
preventClosingOnKey: true,
|
||||
onkey: function(panel) {
|
||||
panel.toolbox.highlighterUtils.togglePicker();
|
||||
onkey: function(panel, toolbox) {
|
||||
toolbox.highlighterUtils.togglePicker();
|
||||
},
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
|
||||
@@ -680,6 +680,9 @@ InspectorPanel.prototype = {
|
||||
!this.selection.isPseudoElementNode();
|
||||
let isEditableElement = isSelectionElement &&
|
||||
!this.selection.isAnonymousNode();
|
||||
let isDuplicatableElement = isSelectionElement &&
|
||||
!this.selection.isAnonymousNode() &&
|
||||
!this.selection.isRoot();
|
||||
let isScreenshotable = isSelectionElement &&
|
||||
this.canGetUniqueSelector &&
|
||||
this.selection.nodeFront.isTreeDisplayed;
|
||||
@@ -709,6 +712,7 @@ InspectorPanel.prototype = {
|
||||
// "Copy outer HTML", "Scroll Into View" & "Screenshot Node" as appropriate
|
||||
let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
|
||||
let screenshot = this.panelDoc.getElementById("node-menu-screenshotnode");
|
||||
let duplicateNode = this.panelDoc.getElementById("node-menu-duplicatenode");
|
||||
let copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner");
|
||||
let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
|
||||
let scrollIntoView = this.panelDoc.getElementById("node-menu-scrollnodeintoview");
|
||||
@@ -725,10 +729,20 @@ InspectorPanel.prototype = {
|
||||
expandAll.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
this._target.actorHasMethod("domwalker", "duplicateNode").then(value => {
|
||||
duplicateNode.hidden = !value;
|
||||
});
|
||||
this._target.actorHasMethod("domnode", "scrollIntoView").then(value => {
|
||||
scrollIntoView.hidden = !value;
|
||||
});
|
||||
|
||||
if (isDuplicatableElement) {
|
||||
duplicateNode.removeAttribute("disabled");
|
||||
}
|
||||
else {
|
||||
duplicateNode.setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
if (isSelectionElement) {
|
||||
unique.removeAttribute("disabled");
|
||||
copyInnerHTML.removeAttribute("disabled");
|
||||
@@ -1239,6 +1253,20 @@ InspectorPanel.prototype = {
|
||||
this.selection.nodeFront.scrollIntoView();
|
||||
},
|
||||
|
||||
/**
|
||||
* Duplicate the selected node
|
||||
*/
|
||||
duplicateNode: function() {
|
||||
let selection = this.selection;
|
||||
if (!selection.isElementNode() ||
|
||||
selection.isRoot() ||
|
||||
selection.isAnonymousNode() ||
|
||||
selection.isPseudoElementNode()) {
|
||||
return;
|
||||
}
|
||||
this.walker.duplicateNode(selection.nodeFront).catch(e => console.error(e));
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the selected node.
|
||||
*/
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
<menuitem id="node-menu-screenshotnode"
|
||||
label="&inspectorScreenshotNode.label;"
|
||||
oncommand="inspector.screenshotNode()" />
|
||||
<menuitem id="node-menu-duplicatenode"
|
||||
label="&inspectorDuplicateNode.label;"
|
||||
oncommand="inspector.duplicateNode()"/>
|
||||
<menuitem id="node-menu-delete"
|
||||
label="&inspectorHTMLDelete.label;"
|
||||
accesskey="&inspectorHTMLDelete.accesskey;"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* global nsContextMenu*/
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Tests that selecting a node using the browser context menu (inspect element)
|
||||
// or the element picker focuses that node so that the keyboard can be used
|
||||
// immediately.
|
||||
@@ -12,22 +13,19 @@
|
||||
const TEST_URL = "data:text/html;charset=utf8,<div>test element</div>";
|
||||
|
||||
add_task(function*() {
|
||||
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Select the test node with the browser ctx menu");
|
||||
yield selectWithBrowserMenu(inspector);
|
||||
yield clickOnInspectMenuItem(testActor, "div");
|
||||
assertNodeSelected(inspector, "div");
|
||||
|
||||
info("Press arrowUp to focus <body> " +
|
||||
"(which works if the node was focused properly)");
|
||||
let onNodeHighlighted = toolbox.once("node-highlight");
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
yield waitForChildrenUpdated(inspector);
|
||||
yield onNodeHighlighted;
|
||||
yield selectPreviousNodeWithArrowUp(inspector);
|
||||
assertNodeSelected(inspector, "body");
|
||||
|
||||
info("Select the test node with the element picker");
|
||||
yield selectWithElementPicker(inspector);
|
||||
yield selectWithElementPicker(inspector, testActor);
|
||||
assertNodeSelected(inspector, "div");
|
||||
|
||||
info("Press arrowUp to focus <body> " +
|
||||
@@ -44,40 +42,20 @@ function assertNodeSelected(inspector, tagName) {
|
||||
`The <${tagName}> node is selected`);
|
||||
}
|
||||
|
||||
function* selectWithBrowserMenu(inspector) {
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("div", {
|
||||
type: "contextmenu",
|
||||
button: 2
|
||||
}, gBrowser.selectedBrowser);
|
||||
|
||||
// nsContextMenu also requires the popupNode to be set, but we can't set it to
|
||||
// node under e10s as it's a CPOW, not a DOM node. But under e10s,
|
||||
// nsContextMenu won't use the property anyway, so just try/catching is ok.
|
||||
try {
|
||||
document.popupNode = getNode("div");
|
||||
} catch (e) {}
|
||||
|
||||
let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu");
|
||||
let contextMenu = new nsContextMenu(contentAreaContextMenu);
|
||||
yield contextMenu.inspectNode();
|
||||
|
||||
contentAreaContextMenu.hidden = true;
|
||||
contentAreaContextMenu.hidePopup();
|
||||
contextMenu.hiding();
|
||||
|
||||
yield inspector.once("inspector-updated");
|
||||
function selectPreviousNodeWithArrowUp(inspector) {
|
||||
let onNodeHighlighted = inspector.toolbox.once("node-highlight");
|
||||
let onUpdated = inspector.once("inspector-updated");
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
return Promise.all([onUpdated, onNodeHighlighted]);
|
||||
}
|
||||
|
||||
function* selectWithElementPicker(inspector) {
|
||||
yield inspector.toolbox.highlighterUtils.startPicker();
|
||||
function* selectWithElementPicker(inspector, testActor) {
|
||||
yield startPicker(inspector.toolbox);
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("div", {
|
||||
type: "mousemove",
|
||||
}, gBrowser.selectedBrowser);
|
||||
|
||||
executeInContent("Test:SynthesizeKey", {
|
||||
key: "VK_RETURN",
|
||||
options: {}
|
||||
}, false);
|
||||
yield testActor.synthesizeKey({key: "VK_RETURN", options: {}});
|
||||
yield inspector.once("inspector-updated");
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ support-files =
|
||||
[browser_rules_completion-popup-hidden-after-navigation.js]
|
||||
[browser_rules_content_01.js]
|
||||
[browser_rules_content_02.js]
|
||||
skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work with e10s
|
||||
skip-if = e10s && debug && os == 'win' # Bug 1250058 - Docshell leak on win debug e10s
|
||||
[browser_rules_context-menu-show-mdn-docs-01.js]
|
||||
[browser_rules_context-menu-show-mdn-docs-02.js]
|
||||
[browser_rules_context-menu-show-mdn-docs-03.js]
|
||||
|
||||
@@ -19,34 +19,12 @@ const STRINGS = Services.strings
|
||||
.createBundle("chrome://devtools-shared/locale/styleinspector.properties");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + CONTENT);
|
||||
let tab = yield addTab("data:text/html;charset=utf-8," + CONTENT);
|
||||
|
||||
info("Getting the test element");
|
||||
let element = getNode("span");
|
||||
let testActor = yield getTestActorWithoutToolbox(tab);
|
||||
let inspector = yield clickOnInspectMenuItem(testActor, "span");
|
||||
|
||||
info("Opening the inspector using the content context-menu");
|
||||
let onInspectorReady = gDevTools.once("inspector-ready");
|
||||
|
||||
document.popupNode = element;
|
||||
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let contextMenu = new nsContextMenu(contentAreaContextMenu);
|
||||
yield contextMenu.inspectNode();
|
||||
|
||||
// Clean up context menu:
|
||||
contextMenu.hiding();
|
||||
|
||||
yield onInspectorReady;
|
||||
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
|
||||
info("Getting the inspector and making sure it is fully updated");
|
||||
let inspector = toolbox.getPanel("inspector");
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
let view = inspector.ruleview.view;
|
||||
|
||||
checkRuleViewContent(view);
|
||||
checkRuleViewContent(inspector.ruleview.view);
|
||||
});
|
||||
|
||||
function checkRuleViewContent({styleDocument}) {
|
||||
@@ -81,3 +59,4 @@ function checkRuleViewContent({styleDocument}) {
|
||||
is(propertyValues.length, 1, "There's only one property value, as expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,9 @@ var openInspector = Task.async(function*() {
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
|
||||
info("Waiting for the inspector to update");
|
||||
yield inspector.once("inspector-updated");
|
||||
if (inspector._updateProgress) {
|
||||
yield inspector.once("inspector-updated");
|
||||
}
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
|
||||
@@ -59,7 +59,8 @@ skip-if = e10s # GCLI isn't e10s compatible. See bug 1128988.
|
||||
[browser_inspector_highlighter-hover_01.js]
|
||||
[browser_inspector_highlighter-hover_02.js]
|
||||
[browser_inspector_highlighter-hover_03.js]
|
||||
[browser_inspector_highlighter-iframes.js]
|
||||
[browser_inspector_highlighter-iframes_01.js]
|
||||
[browser_inspector_highlighter-iframes_02.js]
|
||||
[browser_inspector_highlighter-inline.js]
|
||||
[browser_inspector_highlighter-keybinding_01.js]
|
||||
[browser_inspector_highlighter-keybinding_02.js]
|
||||
@@ -79,6 +80,7 @@ skip-if = e10s # GCLI isn't e10s compatible. See bug 1128988.
|
||||
[browser_inspector_iframe-navigation.js]
|
||||
[browser_inspector_infobar_01.js]
|
||||
[browser_inspector_initialization.js]
|
||||
skip-if = e10s && debug && os == 'win' # Bug 1250058 - Docshell leak on win debug e10s
|
||||
[browser_inspector_inspect-object-element.js]
|
||||
[browser_inspector_invalidate.js]
|
||||
[browser_inspector_keyboard-shortcuts-copy-outerhtml.js]
|
||||
|
||||
@@ -37,7 +37,7 @@ add_task(function* () {
|
||||
yield toolbox.highlighter.showBoxModel(body);
|
||||
|
||||
info("Waiting for element picker to become active.");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Moving mouse over iframe padding.");
|
||||
yield moveMouseOver("iframe", 1, 1);
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ add_task(function*() {
|
||||
let innerFrameDiv = ["iframe", "iframe", "div"];
|
||||
|
||||
info("Waiting for element picker to activate.");
|
||||
yield inspector.toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(inspector.toolbox);
|
||||
|
||||
info("Moving mouse over outerFrameDiv");
|
||||
yield moveMouseOver(testActor, outerFrameDiv);
|
||||
@@ -0,0 +1,56 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// Test that the highlighter is correctly positioned when switching context
|
||||
// to an iframe that has an offset from the parent viewport (eg. 100px margin)
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8," +
|
||||
"<div id=\"outer\"></div>" +
|
||||
"<iframe style='margin:100px' src='data:text/html," +
|
||||
"<div id=\"inner\">Look I am here!</div>'>";
|
||||
|
||||
add_task(function*() {
|
||||
info("Enable command-button-frames preference setting");
|
||||
Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URI);
|
||||
|
||||
info("Switch to the iframe context.");
|
||||
yield switchToFrameContext(1, toolbox, inspector);
|
||||
|
||||
info("Check navigation was successful.");
|
||||
let hasOuterNode = yield testActor.hasNode("#outer");
|
||||
ok(!hasOuterNode, "Check testActor has no access to outer element");
|
||||
let hasTestNode = yield testActor.hasNode("#inner");
|
||||
ok(hasTestNode, "Check testActor has access to inner element");
|
||||
|
||||
info("Check highlighting is correct after switching iframe context");
|
||||
yield selectAndHighlightNode("#inner", inspector);
|
||||
let isHighlightCorrect = yield testActor.assertHighlightedNode("#inner");
|
||||
ok(isHighlightCorrect, "The selected node is properly highlighted.");
|
||||
|
||||
info("Cleanup command-button-frames preferences.");
|
||||
Services.prefs.clearUserPref("devtools.command-button-frames.enabled");
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper designed to switch context to another frame at the provided index.
|
||||
* Returns a promise that will resolve when the navigation is complete.
|
||||
* @return {Promise}
|
||||
*/
|
||||
function* switchToFrameContext(frameIndex, toolbox, inspector) {
|
||||
// Verify that the frame list button is visible and populated
|
||||
let frameListBtn = toolbox.doc.getElementById("command-button-frames");
|
||||
let frameBtns = frameListBtn.firstChild.querySelectorAll("[data-window-id]");
|
||||
|
||||
info("Select the iframe in the frame list.");
|
||||
let newRoot = inspector.once("new-root");
|
||||
frameBtns[frameIndex].click();
|
||||
yield newRoot;
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
info("Navigation to the iframe is done.");
|
||||
}
|
||||
@@ -11,8 +11,7 @@ const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
|
||||
add_task(function*() {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Starting element picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Selecting the simple-div1 DIV");
|
||||
yield moveMouseOver("#simple-div1");
|
||||
|
||||
@@ -11,8 +11,7 @@ const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
|
||||
add_task(function*() {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Starting element picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
// Previously chosen child memory
|
||||
info("Testing whether previously chosen child is remembered");
|
||||
|
||||
@@ -11,8 +11,7 @@ const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
|
||||
add_task(function*() {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Starting element picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Selecting the #another DIV");
|
||||
yield moveMouseOver("#another");
|
||||
@@ -23,8 +22,7 @@ add_task(function*() {
|
||||
is(inspector.selection.nodeFront.id, "another", "The #another node was selected. Passed.");
|
||||
|
||||
// Testing cancel-picker command
|
||||
info("Starting element picker again");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Selecting the ahoy DIV");
|
||||
yield moveMouseOver("#ahoy");
|
||||
|
||||
@@ -12,8 +12,7 @@ const TEST_URL = "data:text/html;charset=utf8,<div></div>";
|
||||
add_task(function*() {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Start the element picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Start using the picker by hovering over nodes");
|
||||
let onHover = toolbox.once("picker-node-hovered");
|
||||
|
||||
@@ -10,8 +10,7 @@ const TEST_URL = URL_ROOT + "doc_inspector_highlighter_xbl.xul";
|
||||
add_task(function*() {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Starting element picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Selecting the scale");
|
||||
yield moveMouseOver("#scale");
|
||||
|
||||
@@ -14,7 +14,7 @@ add_task(function* () {
|
||||
let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URI);
|
||||
|
||||
info("Starting element picker.");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Waiting for highlighter to activate.");
|
||||
let highlighterShowing = toolbox.once("highlighter-ready");
|
||||
|
||||
@@ -114,32 +114,3 @@ function* testBreadcrumbs(selector, inspector) {
|
||||
ok(button, "A crumbs is checked=true");
|
||||
is(button.getAttribute("tooltiptext"), expectedText, "Crumb refers to the right node");
|
||||
}
|
||||
|
||||
function* clickOnInspectMenuItem(testActor, selector) {
|
||||
info("Showing the contextual menu on node " + selector);
|
||||
yield testActor.synthesizeMouse({
|
||||
selector: selector,
|
||||
center: true,
|
||||
options: {type: "contextmenu", button: 2}
|
||||
});
|
||||
|
||||
// nsContextMenu also requires the popupNode to be set, but we can't set it to
|
||||
// node under e10s as it's a CPOW, not a DOM node. But under e10s,
|
||||
// nsContextMenu won't use the property anyway, so just try/catching is ok.
|
||||
try {
|
||||
document.popupNode = content.document.querySelector(selector);
|
||||
} catch (e) {}
|
||||
|
||||
let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu");
|
||||
let contextMenu = new nsContextMenu(contentAreaContextMenu);
|
||||
|
||||
info("Triggering inspect action and hiding the menu.");
|
||||
yield contextMenu.inspectNode();
|
||||
|
||||
contentAreaContextMenu.hidden = true;
|
||||
contentAreaContextMenu.hidePopup();
|
||||
contextMenu.hiding();
|
||||
|
||||
info("Waiting for inspector to update.");
|
||||
yield getActiveInspector().once("inspector-updated");
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ add_task(function* () {
|
||||
yield selectNode("p", inspector);
|
||||
|
||||
info("Inspector displayed and ready, starting the picker.");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Destroying the toolbox.");
|
||||
yield toolbox.destroy();
|
||||
|
||||
@@ -15,7 +15,7 @@ add_task(function* () {
|
||||
let pickerStopped = toolbox.once("picker-stopped");
|
||||
|
||||
info("Starting the inspector picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield startPicker(toolbox);
|
||||
|
||||
info("Selecting another tool than the inspector in the toolbox");
|
||||
yield toolbox.selectNextTool();
|
||||
|
||||
@@ -89,6 +89,20 @@ function getNode(nodeOrSelector, options = {}) {
|
||||
return nodeOrSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the element picker and focus the content window.
|
||||
* @param {Toolbox} toolbox
|
||||
*/
|
||||
var startPicker = Task.async(function*(toolbox) {
|
||||
info("Start the element picker");
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
// Make sure the content window is focused since the picker does not focus
|
||||
// the content window by default.
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
|
||||
content.focus();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Highlight a node and set the inspector's current selection to the node or
|
||||
* the first match of the given css selector.
|
||||
@@ -159,6 +173,36 @@ function getActiveInspector() {
|
||||
return gDevTools.getToolbox(target).getPanel("inspector");
|
||||
}
|
||||
|
||||
/**
|
||||
* Right click on a node in the test page and click on the inspect menu item.
|
||||
* @param {TestActor}
|
||||
* @param {String} selector The selector for the node to click on in the page.
|
||||
* @return {Promise} Resolves to the inspector when it has opened and is updated
|
||||
*/
|
||||
var clickOnInspectMenuItem = Task.async(function*(testActor, selector) {
|
||||
info("Showing the contextual menu on node " + selector);
|
||||
let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu");
|
||||
let contextOpened = once(contentAreaContextMenu, "popupshown");
|
||||
|
||||
yield testActor.synthesizeMouse({
|
||||
selector: selector,
|
||||
center: true,
|
||||
options: {type: "contextmenu", button: 2}
|
||||
});
|
||||
|
||||
yield contextOpened;
|
||||
|
||||
info("Triggering the inspect action");
|
||||
yield gContextMenu.inspectNode();
|
||||
|
||||
info("Hiding the menu");
|
||||
let contextClosed = once(contentAreaContextMenu, "popuphidden");
|
||||
contentAreaContextMenu.hidePopup();
|
||||
yield contextClosed;
|
||||
|
||||
return getActiveInspector();
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible, and the one of the sidebar
|
||||
* tabs selected.
|
||||
|
||||
@@ -194,10 +194,7 @@ devtools.jar:
|
||||
skin/images/command-measure@2x.png (themes/images/command-measure@2x.png)
|
||||
skin/markup.css (themes/markup.css)
|
||||
skin/images/editor-error.png (themes/images/editor-error.png)
|
||||
skin/images/editor-breakpoint.png (themes/images/editor-breakpoint.png)
|
||||
skin/images/editor-breakpoint@2x.png (themes/images/editor-breakpoint@2x.png)
|
||||
skin/images/editor-debug-location.png (themes/images/editor-debug-location.png)
|
||||
skin/images/editor-debug-location@2x.png (themes/images/editor-debug-location@2x.png)
|
||||
skin/images/breakpoint.svg (themes/images/breakpoint.svg)
|
||||
skin/webconsole.css (themes/webconsole.css)
|
||||
skin/images/webconsole.svg (themes/images/webconsole.svg)
|
||||
skin/images/breadcrumbs-divider@2x.png (themes/images/breadcrumbs-divider@2x.png)
|
||||
@@ -316,6 +313,7 @@ devtools.jar:
|
||||
skin/images/tool-memory-active.svg (themes/images/tool-memory-active.svg)
|
||||
skin/images/close.png (themes/images/close.png)
|
||||
skin/images/close@2x.png (themes/images/close@2x.png)
|
||||
skin/images/clear.svg (themes/images/clear.svg)
|
||||
skin/images/vview-delete.png (themes/images/vview-delete.png)
|
||||
skin/images/vview-delete@2x.png (themes/images/vview-delete@2x.png)
|
||||
skin/images/vview-edit.png (themes/images/vview-edit.png)
|
||||
@@ -350,3 +348,4 @@ devtools.jar:
|
||||
skin/images/security-state-local.svg (themes/images/security-state-local.svg)
|
||||
skin/images/security-state-secure.svg (themes/images/security-state-secure.svg)
|
||||
skin/images/security-state-weak.svg (themes/images/security-state-weak.svg)
|
||||
skin/images/diff.svg (themes/images/diff.svg)
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
sidebar tab -->
|
||||
<!ENTITY animationInspectorTitle "Animations">
|
||||
|
||||
<!-- LOCALIZATION NOTE (invalidElement): This is the label shown in the panel
|
||||
when an invalid node is currently selected in the inspector. -->
|
||||
<!ENTITY invalidElement "No animations were found for the current element.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (selectElement): This is the label shown in the panel
|
||||
when an invalid node is currently selected in the inspector, to invite the
|
||||
user to select a new node by clicking on the element-picker icon. -->
|
||||
|
||||
@@ -10,6 +10,18 @@
|
||||
# A good criteria is the language in which you'd find the best
|
||||
# documentation on web development on the web.
|
||||
|
||||
# LOCALIZATION NOTE (panel.invalidElementSelected):
|
||||
# This is the label shown in the panel when an invalid node is currently
|
||||
# selected in the inspector (i.e. a non-element node or a node that is not
|
||||
# animated).
|
||||
panel.invalidElementSelected=No animations were found for the current element.
|
||||
|
||||
# LOCALIZATION NOTE (panel.pseudoElementSelected):
|
||||
# This is the label shown in the panel when a pseudo-element is currently
|
||||
# selected in the inspector (pseudo-elements can be animated, but the tool
|
||||
# doesn't yet support them).
|
||||
panel.pseudoElementSelected=Animated pseudo-elements are not supported yet.
|
||||
|
||||
# LOCALIZATION NOTE (player.animationNameLabel):
|
||||
# This string is displayed in each animation player widget. It is the label
|
||||
# displayed before the animation name.
|
||||
@@ -61,14 +73,35 @@ player.timeLabel=%Ss
|
||||
# LOCALIZATION NOTE (player.playbackRateLabel):
|
||||
# This string is displayed in each animation player widget, as the label of
|
||||
# drop-down list items that can be used to change the rate at which the
|
||||
# animation runs (1x being the default, 2x being twice as fast).
|
||||
player.playbackRateLabel=%Sx
|
||||
# animation runs (1× being the default, 2× being twice as fast).
|
||||
player.playbackRateLabel=%S×
|
||||
|
||||
# LOCALIZATION NOTE (player.runningOnCompositorTooltip):
|
||||
# This string is displayed as a tooltip for the icon that indicates that the
|
||||
# animation is running on the compositor thread.
|
||||
player.runningOnCompositorTooltip=This animation is running on compositor thread
|
||||
|
||||
# LOCALIZATION NOTE (timeline.rateSelectorTooltip):
|
||||
# This string is displayed in the timeline toolbar, as the tooltip of the
|
||||
# drop-down list that can be used to change the rate at which the animations
|
||||
# run.
|
||||
timeline.rateSelectorTooltip=Set the animations playback rates
|
||||
|
||||
# LOCALIZATION NOTE (timeline.pauseResumeButtonTooltip):
|
||||
# This string is displayed in the timeline toolbar, as the tooltip of the
|
||||
# pause/resume button that can be used to pause or resume the animations
|
||||
timeline.pausedButtonTooltip=Resume the animations
|
||||
|
||||
# LOCALIZATION NOTE (timeline.pauseResumeButtonTooltip):
|
||||
# This string is displayed in the timeline toolbar, as the tooltip of the
|
||||
# pause/resume button that can be used to pause or resume the animations
|
||||
timeline.resumedButtonTooltip=Pause the animations
|
||||
|
||||
# LOCALIZATION NOTE (timeline.rewindButtonTooltip):
|
||||
# This string is displayed in the timeline toolbar, as the tooltip of the
|
||||
# rewind button that can be used to rewind the animations
|
||||
timeline.rewindButtonTooltip=Rewind the animations
|
||||
|
||||
# LOCALIZATION NOTE (timeline.timeGraduationLabel):
|
||||
# This string is displayed at the top of the animation panel, next to each time
|
||||
# graduation, to indicate what duration (in milliseconds) this graduation
|
||||
@@ -87,6 +120,17 @@ timeline.cssanimation.nameLabel=%S - CSS Animation
|
||||
# %S will be replaced by the name of the transition at run-time.
|
||||
timeline.csstransition.nameLabel=%S - CSS Transition
|
||||
|
||||
# LOCALIZATION NOTE (timeline.scriptanimation.nameLabel):
|
||||
# This string is displayed in a tooltip of the animation panel that is shown
|
||||
# when hovering over the name of a script-generated animation in the timeline UI.
|
||||
# %S will be replaced by the name of the animation at run-time.
|
||||
timeline.scriptanimation.nameLabel=%S - Script Animation
|
||||
|
||||
# LOCALIZATION NOTE (timeline.scriptanimation.unnamedLabel):
|
||||
# This string is displayed in a tooltip of the animation panel that is shown
|
||||
# when hovering over an unnamed script-generated animation in the timeline UI.
|
||||
timeline.scriptanimation.unnamedLabel=Script Animation
|
||||
|
||||
# LOCALIZATION NOTE (timeline.unknown.nameLabel):
|
||||
# This string is displayed in a tooltip of the animation panel that is shown
|
||||
# when hovering over the name of an unknown animation type in the timeline UI.
|
||||
|
||||
@@ -150,3 +150,8 @@
|
||||
shown in the inspector contextual-menu for the item that lets users take
|
||||
a screenshot of the currently selected node. -->
|
||||
<!ENTITY inspectorScreenshotNode.label "Screenshot Node">
|
||||
|
||||
<!-- LOCALIZATION NOTE (inspectorDuplicateNode.label): This is the label
|
||||
shown in the inspector contextual-menu for the item that lets users
|
||||
duplicate the currently selected node. -->
|
||||
<!ENTITY inspectorDuplicateNode.label "Duplicate Node">
|
||||
|
||||
@@ -26,6 +26,10 @@ jit.optimizationFailure=Optimization failed
|
||||
# example: 30 samples
|
||||
jit.samples=#1 sample;#1 samples
|
||||
|
||||
# LOCALIZATION NOTE (jit.empty):
|
||||
# This string is displayed when there are no JIT optimizations to display.
|
||||
jit.empty=No JIT optimizations recorded for this frame.
|
||||
# LOCALIZATION NOTE (jit.types):
|
||||
# This string is displayed for the group of Ion Types in the optimizations view.
|
||||
jit.types=Types
|
||||
|
||||
# LOCALIZATION NOTE (jit.attempts):
|
||||
# This string is displayed for the group of optimization attempts in the optimizations view.
|
||||
jit.attempts=Attempts
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user