Bug 1837620 - Part 7: Keep JitScripts in a linked list to avoid iterating all BaseScr...
[gecko.git] / widget / ContentCache.h
blob332ff9ff43125c8f2b1be3abca0c11e02418d6a6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #ifndef mozilla_ContentCache_h
9 #define mozilla_ContentCache_h
11 #include <stdint.h>
13 #include "mozilla/widget/IMEData.h"
14 #include "mozilla/ipc/IPCForwards.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/CheckedInt.h"
17 #include "mozilla/EventForwards.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/ToString.h"
20 #include "mozilla/WritingModes.h"
21 #include "nsString.h"
22 #include "nsTArray.h"
23 #include "Units.h"
25 class nsIWidget;
27 namespace mozilla {
29 class ContentCacheInParent;
31 namespace dom {
32 class BrowserParent;
33 } // namespace dom
35 /**
36 * ContentCache stores various information of the child content.
37 * This class has members which are necessary both in parent process and
38 * content process.
41 class ContentCache {
42 public:
43 using RectArray = CopyableTArray<LayoutDeviceIntRect>;
44 using IMENotification = widget::IMENotification;
46 ContentCache() = default;
48 [[nodiscard]] bool IsValid() const;
50 protected:
51 // Whole text in the target
52 Maybe<nsString> mText;
54 // Start offset of the composition string.
55 Maybe<uint32_t> mCompositionStart;
57 enum { ePrevCharRect = 1, eNextCharRect = 0 };
59 struct Selection final {
60 // Following values are offset in "flat text".
61 uint32_t mAnchor;
62 uint32_t mFocus;
64 WritingMode mWritingMode;
66 bool mHasRange;
68 // Character rects at previous and next character of mAnchor and mFocus.
69 // The reason why ContentCache needs to store each previous character of
70 // them is IME may query character rect of the last character of a line
71 // when caret is at the end of the line.
72 // Note that use ePrevCharRect and eNextCharRect for accessing each item.
73 LayoutDeviceIntRect mAnchorCharRects[2];
74 LayoutDeviceIntRect mFocusCharRects[2];
76 // Whole rect of selected text. This is empty if the selection is collapsed.
77 LayoutDeviceIntRect mRect;
79 Selection() : mAnchor(UINT32_MAX), mFocus(UINT32_MAX), mHasRange(false) {
80 ClearRects();
83 explicit Selection(
84 const IMENotification::SelectionChangeDataBase& aSelectionChangeData)
85 : mAnchor(UINT32_MAX),
86 mFocus(UINT32_MAX),
87 mWritingMode(aSelectionChangeData.GetWritingMode()),
88 mHasRange(aSelectionChangeData.HasRange()) {
89 if (mHasRange) {
90 mAnchor = aSelectionChangeData.AnchorOffset();
91 mFocus = aSelectionChangeData.FocusOffset();
95 [[nodiscard]] bool IsValidIn(const nsAString& aText) const {
96 return !mHasRange ||
97 (mAnchor <= aText.Length() && mFocus <= aText.Length());
100 explicit Selection(const WidgetQueryContentEvent& aQuerySelectedTextEvent);
102 void ClearRects() {
103 for (auto& rect : mAnchorCharRects) {
104 rect.SetEmpty();
106 for (auto& rect : mFocusCharRects) {
107 rect.SetEmpty();
109 mRect.SetEmpty();
111 bool HasRects() const {
112 for (const auto& rect : mAnchorCharRects) {
113 if (!rect.IsEmpty()) {
114 return true;
117 for (const auto& rect : mFocusCharRects) {
118 if (!rect.IsEmpty()) {
119 return true;
122 return !mRect.IsEmpty();
125 bool IsCollapsed() const { return !mHasRange || mFocus == mAnchor; }
126 bool Reversed() const {
127 MOZ_ASSERT(mHasRange);
128 return mFocus < mAnchor;
130 uint32_t StartOffset() const {
131 MOZ_ASSERT(mHasRange);
132 return Reversed() ? mFocus : mAnchor;
134 uint32_t EndOffset() const {
135 MOZ_ASSERT(mHasRange);
136 return Reversed() ? mAnchor : mFocus;
138 uint32_t Length() const {
139 MOZ_ASSERT(mHasRange);
140 return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
142 LayoutDeviceIntRect StartCharRect() const {
143 return Reversed() ? mFocusCharRects[eNextCharRect]
144 : mAnchorCharRects[eNextCharRect];
146 LayoutDeviceIntRect EndCharRect() const {
147 return Reversed() ? mAnchorCharRects[eNextCharRect]
148 : mFocusCharRects[eNextCharRect];
151 friend std::ostream& operator<<(std::ostream& aStream,
152 const Selection& aSelection) {
153 aStream << "{ ";
154 if (!aSelection.mHasRange) {
155 aStream << "HasRange()=false";
156 } else {
157 aStream << "mAnchor=" << aSelection.mAnchor
158 << ", mFocus=" << aSelection.mFocus << ", mWritingMode="
159 << ToString(aSelection.mWritingMode).c_str();
161 if (aSelection.HasRects()) {
162 if (aSelection.mAnchor > 0) {
163 aStream << ", mAnchorCharRects[ePrevCharRect]="
164 << aSelection.mAnchorCharRects[ContentCache::ePrevCharRect];
166 aStream << ", mAnchorCharRects[eNextCharRect]="
167 << aSelection.mAnchorCharRects[ContentCache::eNextCharRect];
168 if (aSelection.mFocus > 0) {
169 aStream << ", mFocusCharRects[ePrevCharRect]="
170 << aSelection.mFocusCharRects[ContentCache::ePrevCharRect];
172 aStream << ", mFocusCharRects[eNextCharRect]="
173 << aSelection.mFocusCharRects[ContentCache::eNextCharRect]
174 << ", mRect=" << aSelection.mRect;
176 if (aSelection.mHasRange) {
177 aStream << ", Reversed()=" << (aSelection.Reversed() ? "true" : "false")
178 << ", StartOffset()=" << aSelection.StartOffset()
179 << ", EndOffset()=" << aSelection.EndOffset()
180 << ", IsCollapsed()="
181 << (aSelection.IsCollapsed() ? "true" : "false")
182 << ", Length()=" << aSelection.Length();
184 aStream << " }";
185 return aStream;
188 Maybe<Selection> mSelection;
190 // Stores first char rect because Yosemite's Japanese IME sometimes tries
191 // to query it. If there is no text, this is caret rect.
192 LayoutDeviceIntRect mFirstCharRect;
194 struct Caret final {
195 uint32_t mOffset = 0u;
196 LayoutDeviceIntRect mRect;
198 explicit Caret(uint32_t aOffset, LayoutDeviceIntRect aCaretRect)
199 : mOffset(aOffset), mRect(aCaretRect) {}
201 uint32_t Offset() const { return mOffset; }
202 bool HasRect() const { return !mRect.IsEmpty(); }
204 [[nodiscard]] bool IsValidIn(const nsAString& aText) const {
205 return mOffset <= aText.Length();
208 friend std::ostream& operator<<(std::ostream& aStream,
209 const Caret& aCaret) {
210 aStream << "{ mOffset=" << aCaret.mOffset;
211 if (aCaret.HasRect()) {
212 aStream << ", mRect=" << aCaret.mRect;
214 return aStream << " }";
217 private:
218 // For ParamTraits<Caret>
219 Caret() = default;
221 friend struct IPC::ParamTraits<ContentCache::Caret>;
222 ALLOW_DEPRECATED_READPARAM
224 Maybe<Caret> mCaret;
226 struct TextRectArray final {
227 uint32_t mStart = 0u;
228 RectArray mRects;
230 explicit TextRectArray(uint32_t aStartOffset) : mStart(aStartOffset) {}
232 bool HasRects() const { return Length() > 0; }
233 uint32_t StartOffset() const { return mStart; }
234 uint32_t EndOffset() const {
235 CheckedInt<uint32_t> endOffset =
236 CheckedInt<uint32_t>(mStart) + mRects.Length();
237 return endOffset.isValid() ? endOffset.value() : UINT32_MAX;
239 uint32_t Length() const { return EndOffset() - mStart; }
240 bool IsOffsetInRange(uint32_t aOffset) const {
241 return StartOffset() <= aOffset && aOffset < EndOffset();
243 bool IsRangeCompletelyInRange(uint32_t aOffset, uint32_t aLength) const {
244 CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength;
245 if (NS_WARN_IF(!endOffset.isValid())) {
246 return false;
248 return IsOffsetInRange(aOffset) && aOffset + aLength <= EndOffset();
250 bool IsOverlappingWith(uint32_t aOffset, uint32_t aLength) const {
251 if (!HasRects() || aOffset == UINT32_MAX || !aLength) {
252 return false;
254 CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength;
255 if (NS_WARN_IF(!endOffset.isValid())) {
256 return false;
258 return aOffset < EndOffset() && endOffset.value() > mStart;
260 LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
261 LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
262 LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
263 uint32_t aOffset, uint32_t aLength, bool aRoundToExistingOffset) const;
265 friend std::ostream& operator<<(std::ostream& aStream,
266 const TextRectArray& aTextRectArray) {
267 aStream << "{ mStart=" << aTextRectArray.mStart
268 << ", mRects={ Length()=" << aTextRectArray.Length();
269 if (aTextRectArray.HasRects()) {
270 aStream << ", Elements()=[ ";
271 static constexpr uint32_t kMaxPrintRects = 4;
272 const uint32_t kFirstHalf = aTextRectArray.Length() <= kMaxPrintRects
273 ? UINT32_MAX
274 : (kMaxPrintRects + 1) / 2;
275 const uint32_t kSecondHalf =
276 aTextRectArray.Length() <= kMaxPrintRects ? 0 : kMaxPrintRects / 2;
277 for (uint32_t i = 0; i < aTextRectArray.Length(); i++) {
278 if (i > 0) {
279 aStream << ", ";
281 aStream << ToString(aTextRectArray.mRects[i]).c_str();
282 if (i + 1 == kFirstHalf) {
283 aStream << " ...";
284 i = aTextRectArray.Length() - kSecondHalf - 1;
288 return aStream << " ] } }";
291 private:
292 // For ParamTraits<TextRectArray>
293 TextRectArray() = default;
295 friend struct IPC::ParamTraits<ContentCache::TextRectArray>;
296 ALLOW_DEPRECATED_READPARAM
298 Maybe<TextRectArray> mTextRectArray;
299 Maybe<TextRectArray> mLastCommitStringTextRectArray;
301 LayoutDeviceIntRect mEditorRect;
303 friend class ContentCacheInParent;
304 friend struct IPC::ParamTraits<ContentCache>;
305 friend struct IPC::ParamTraits<ContentCache::Selection>;
306 friend struct IPC::ParamTraits<ContentCache::Caret>;
307 friend struct IPC::ParamTraits<ContentCache::TextRectArray>;
308 friend std::ostream& operator<<(
309 std::ostream& aStream,
310 const Selection& aSelection); // For e(Prev|Next)CharRect
311 ALLOW_DEPRECATED_READPARAM
314 class ContentCacheInChild final : public ContentCache {
315 public:
316 ContentCacheInChild() = default;
319 * Called when composition event will be dispatched in this process from
320 * PuppetWidget.
322 void OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
325 * When IME loses focus, this should be called and making this forget the
326 * content for reducing footprint.
328 void Clear();
331 * Cache*() retrieves the latest content information and store them.
332 * Be aware, CacheSelection() calls CacheCaretAndTextRects(),
333 * CacheCaretAndTextRects() calls CacheCaret() and CacheTextRects(), and
334 * CacheText() calls CacheSelection(). So, related data is also retrieved
335 * automatically.
337 bool CacheEditorRect(nsIWidget* aWidget,
338 const IMENotification* aNotification = nullptr);
339 bool CacheCaretAndTextRects(nsIWidget* aWidget,
340 const IMENotification* aNotification = nullptr);
341 bool CacheText(nsIWidget* aWidget,
342 const IMENotification* aNotification = nullptr);
344 bool CacheAll(nsIWidget* aWidget,
345 const IMENotification* aNotification = nullptr);
348 * SetSelection() modifies selection with specified raw data. And also this
349 * tries to retrieve text rects too.
351 * @return true if the selection is cached. Otherwise, false.
353 [[nodiscard]] bool SetSelection(
354 nsIWidget* aWidget,
355 const IMENotification::SelectionChangeDataBase& aSelectionChangeData);
357 private:
358 bool QueryCharRect(nsIWidget* aWidget, uint32_t aOffset,
359 LayoutDeviceIntRect& aCharRect) const;
360 bool QueryCharRectArray(nsIWidget* aWidget, uint32_t aOffset,
361 uint32_t aLength, RectArray& aCharRectArray) const;
362 bool CacheSelection(nsIWidget* aWidget,
363 const IMENotification* aNotification = nullptr);
364 bool CacheCaret(nsIWidget* aWidget,
365 const IMENotification* aNotification = nullptr);
366 bool CacheTextRects(nsIWidget* aWidget,
367 const IMENotification* aNotification = nullptr);
369 // Once composition is committed, all of the commit string may be composed
370 // again by Kakutei-Undo of Japanese IME. Therefore, we need to keep
371 // storing the last composition start to cache all character rects of the
372 // last commit string.
373 Maybe<OffsetAndData<uint32_t>> mLastCommit;
376 class ContentCacheInParent final : public ContentCache {
377 public:
378 ContentCacheInParent() = delete;
379 explicit ContentCacheInParent(dom::BrowserParent& aBrowserParent);
382 * AssignContent() is called when BrowserParent receives ContentCache from
383 * the content process. This doesn't copy composition information because
384 * it's managed by BrowserParent itself.
386 void AssignContent(const ContentCache& aOther, nsIWidget* aWidget,
387 const IMENotification* aNotification = nullptr);
390 * HandleQueryContentEvent() sets content data to aEvent.mReply.
392 * For eQuerySelectedText, fail if the cache doesn't contain the whole
393 * selected range. (This shouldn't happen because PuppetWidget should have
394 * already sent the whole selection.)
396 * For eQueryTextContent, fail only if the cache doesn't overlap with
397 * the queried range. Note the difference from above. We use
398 * this behavior because a normal eQueryTextContent event is allowed to
399 * have out-of-bounds offsets, so that widget can request content without
400 * knowing the exact length of text. It's up to widget to handle cases when
401 * the returned offset/length are different from the queried offset/length.
403 * For eQueryTextRect, fail if cached offset/length aren't equals to input.
404 * Cocoa widget always queries selected offset, so it works on it.
406 * For eQueryCaretRect, fail if cached offset isn't equals to input
408 * For eQueryEditorRect, always success
410 bool HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
411 nsIWidget* aWidget) const;
414 * OnCompositionEvent() should be called before sending composition string.
415 * This returns true if the event should be sent. Otherwise, false.
417 bool OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
420 * OnSelectionEvent() should be called before sending selection event.
422 void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
425 * OnEventNeedingAckHandled() should be called after the child process
426 * handles a sent event which needs acknowledging.
428 * WARNING: This may send notifications to IME. That might cause destroying
429 * BrowserParent or aWidget. Therefore, the caller must not destroy
430 * this instance during a call of this method.
432 void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage,
433 uint32_t aCompositionId);
436 * RequestIMEToCommitComposition() requests aWidget to commit or cancel
437 * composition. If it's handled synchronously, this returns true.
439 * @param aWidget The widget to be requested to commit or cancel
440 * the composition.
441 * @param aCancel When the caller tries to cancel the composition, true.
442 * Otherwise, i.e., tries to commit the composition, false.
443 * @param aCompositionId
444 * The composition ID which should be committed or
445 * canceled.
446 * @param aCommittedString The committed string (i.e., the last data of
447 * dispatched composition events during requesting
448 * IME to commit composition.
449 * @return Whether the composition is actually committed
450 * synchronously.
452 bool RequestIMEToCommitComposition(nsIWidget* aWidget, bool aCancel,
453 uint32_t aCompositionId,
454 nsAString& aCommittedString);
457 * MaybeNotifyIME() may notify IME of the notification. If child process
458 * hasn't been handled all sending events yet, this stores the notification
459 * and flush it later.
461 void MaybeNotifyIME(nsIWidget* aWidget, const IMENotification& aNotification);
463 private:
464 struct HandlingCompositionData;
466 // Return true when the widget in this process thinks that IME has
467 // composition. So, this returns true when there is at least one handling
468 // composition data and the last handling composition has not dispatched
469 // composition commit event to the remote process yet.
470 [[nodiscard]] bool WidgetHasComposition() const {
471 return !mHandlingCompositions.IsEmpty() &&
472 !mHandlingCompositions.LastElement().mSentCommitEvent;
475 // Return true if there is a pending composition which has already sent
476 // a commit event to the remote process, but not yet handled by it.
477 [[nodiscard]] bool HasPendingCommit() const {
478 for (const HandlingCompositionData& data : mHandlingCompositions) {
479 if (data.mSentCommitEvent) {
480 return true;
483 return false;
486 // Return the number of composition events and set selection events which were
487 // sent to the remote process, but we've not verified that the remote process
488 // finished handling it.
489 [[nodiscard]] uint32_t PendingEventsNeedingAck() const {
490 uint32_t ret = mPendingSetSelectionEventNeedingAck;
491 for (const HandlingCompositionData& data : mHandlingCompositions) {
492 ret += data.mPendingEventsNeedingAck;
494 return ret;
497 [[nodiscard]] HandlingCompositionData* GetHandlingCompositionData(
498 uint32_t aCompositionId) {
499 for (HandlingCompositionData& data : mHandlingCompositions) {
500 if (data.mCompositionId == aCompositionId) {
501 return &data;
504 return nullptr;
506 [[nodiscard]] const HandlingCompositionData* GetHandlingCompositionData(
507 uint32_t aCompositionId) const {
508 return const_cast<ContentCacheInParent*>(this)->GetHandlingCompositionData(
509 aCompositionId);
512 IMENotification mPendingSelectionChange;
513 IMENotification mPendingTextChange;
514 IMENotification mPendingLayoutChange;
515 IMENotification mPendingCompositionUpdate;
517 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
518 // Log of event messages to be output to crash report.
519 nsTArray<EventMessage> mDispatchedEventMessages;
520 nsTArray<EventMessage> mReceivedEventMessages;
521 // Log of RequestIMEToCommitComposition() in the last 2 compositions.
522 enum class RequestIMEToCommitCompositionResult : uint8_t {
523 eToOldCompositionReceived,
524 eToUnknownCompositionReceived,
525 eToCommittedCompositionReceived,
526 eReceivedAfterBrowserParentBlur,
527 eReceivedButNoTextComposition,
528 eReceivedButForDifferentTextComposition,
529 eHandledAsynchronously,
530 eHandledSynchronously,
532 const char* ToReadableText(
533 RequestIMEToCommitCompositionResult aResult) const {
534 switch (aResult) {
535 case RequestIMEToCommitCompositionResult::eToOldCompositionReceived:
536 return "Commit request is not handled because it's for "
537 "older composition";
538 case RequestIMEToCommitCompositionResult::eToUnknownCompositionReceived:
539 return "Commit request is not handled because it's for "
540 "unknown composition";
541 case RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived:
542 return "Commit request is not handled because BrowserParent has "
543 "already "
544 "sent commit event for the composition";
545 case RequestIMEToCommitCompositionResult::eReceivedAfterBrowserParentBlur:
546 return "Commit request is handled with stored composition string "
547 "because BrowserParent has already lost focus";
548 case RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition:
549 return "Commit request is not handled because there is no "
550 "TextComposition instance";
551 case RequestIMEToCommitCompositionResult::
552 eReceivedButForDifferentTextComposition:
553 return "Commit request is handled with stored composition string "
554 "because new TextComposition is active";
555 case RequestIMEToCommitCompositionResult::eHandledAsynchronously:
556 return "Commit request is handled but IME doesn't commit current "
557 "composition synchronously";
558 case RequestIMEToCommitCompositionResult::eHandledSynchronously:
559 return "Commit request is handled synchronously";
560 default:
561 return "Unknown reason";
564 nsTArray<RequestIMEToCommitCompositionResult>
565 mRequestIMEToCommitCompositionResults;
566 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
568 // Stores pending compositions (meaning eCompositionStart was dispatched, but
569 // eCompositionCommit(AsIs) has not been handled by the remote process yet).
570 struct HandlingCompositionData {
571 // The lasted composition string which was sent to the remote process.
572 nsString mCompositionString;
573 // The composition ID of a handling composition with the instance.
574 uint32_t mCompositionId;
575 // Increased when sending composition events and decreased when the
576 // remote process finished handling the events.
577 uint32_t mPendingEventsNeedingAck = 0u;
578 // true if eCompositionCommit(AsIs) has already been sent to the remote
579 // process.
580 bool mSentCommitEvent = false;
582 explicit HandlingCompositionData(uint32_t aCompositionId)
583 : mCompositionId(aCompositionId) {}
585 AutoTArray<HandlingCompositionData, 2> mHandlingCompositions;
587 // mBrowserParent is owner of the instance.
588 dom::BrowserParent& MOZ_NON_OWNING_REF mBrowserParent;
589 // This is not nullptr only while the instance is requesting IME to
590 // composition. Then, data value of dispatched composition events should
591 // be stored into the instance.
592 nsAString* mCommitStringByRequest;
593 // mCompositionStartInChild stores current composition start offset in the
594 // remote process.
595 Maybe<uint32_t> mCompositionStartInChild;
596 // Increased when sending eSetSelection events and decreased when the remote
597 // process finished handling the events. Note that eSetSelection may be
598 // dispatched without composition. Therefore, we need to count it with this.
599 uint32_t mPendingSetSelectionEventNeedingAck = 0u;
600 // mPendingCommitLength is commit string length of the first pending
601 // composition. This is used by relative offset query events when querying
602 // new composition start offset.
603 // Note that when mHandlingCompositions has 2 or more elements, i.e., there
604 // are 2 or more pending compositions, this cache won't be used because in
605 // such case, anyway ContentCacheInParent cannot return proper character rect.
606 uint32_t mPendingCommitLength;
607 // mIsChildIgnoringCompositionEvents is set to true if the child process
608 // requests commit composition whose commit has already been sent to it.
609 // Then, set to false when the child process ignores the commit event.
610 bool mIsChildIgnoringCompositionEvents;
613 * When following methods' aRoundToExistingOffset is true, even if specified
614 * offset or range is out of bounds, the result is computed with the existing
615 * cache forcibly.
617 bool GetCaretRect(uint32_t aOffset, bool aRoundToExistingOffset,
618 LayoutDeviceIntRect& aCaretRect) const;
619 bool GetTextRect(uint32_t aOffset, bool aRoundToExistingOffset,
620 LayoutDeviceIntRect& aTextRect) const;
621 bool GetUnionTextRects(uint32_t aOffset, uint32_t aLength,
622 bool aRoundToExistingOffset,
623 LayoutDeviceIntRect& aUnionTextRect) const;
625 void FlushPendingNotifications(nsIWidget* aWidget);
627 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
629 * Remove unnecessary messages from mDispatchedEventMessages and
630 * mReceivedEventMessages.
632 void RemoveUnnecessaryEventMessageLog();
635 * Append event message log to aLog.
637 void AppendEventMessageLog(nsACString& aLog) const;
638 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
641 } // namespace mozilla
643 #endif // mozilla_ContentCache_h