1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:ts=2:et:sw=2:
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Foundation code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2008
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * John Daggett <jdaggett@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /* code for loading in @font-face defined font data */
41 :
42 : #ifdef MOZ_LOGGING
43 : #define FORCE_PR_LOG /* Allow logging in the release build */
44 : #endif /* MOZ_LOGGING */
45 : #include "prlog.h"
46 :
47 : #include "nsFontFaceLoader.h"
48 :
49 : #include "nsError.h"
50 : #include "nsIFile.h"
51 : #include "nsILocalFile.h"
52 : #include "nsIStreamListener.h"
53 : #include "nsNetUtil.h"
54 : #include "nsIChannelEventSink.h"
55 : #include "nsIInterfaceRequestor.h"
56 : #include "nsContentUtils.h"
57 : #include "mozilla/Preferences.h"
58 :
59 : #include "nsPresContext.h"
60 : #include "nsIPresShell.h"
61 : #include "nsIDocument.h"
62 : #include "nsIFrame.h"
63 : #include "nsIPrincipal.h"
64 : #include "nsIScriptSecurityManager.h"
65 :
66 : #include "nsDirectoryServiceUtils.h"
67 : #include "nsDirectoryServiceDefs.h"
68 : #include "nsIContentPolicy.h"
69 : #include "nsContentPolicyUtils.h"
70 : #include "nsContentErrors.h"
71 : #include "nsCrossSiteListenerProxy.h"
72 : #include "nsIContentSecurityPolicy.h"
73 : #include "nsIChannelPolicy.h"
74 : #include "nsChannelPolicy.h"
75 :
76 : #include "nsIConsoleService.h"
77 :
78 : #include "nsStyleSet.h"
79 : #include "nsPrintfCString.h"
80 :
81 : using namespace mozilla;
82 :
83 : #ifdef PR_LOGGING
84 1464 : static PRLogModuleInfo *gFontDownloaderLog = PR_NewLogModule("fontdownloader");
85 : #endif /* PR_LOGGING */
86 :
87 : #define LOG(args) PR_LOG(gFontDownloaderLog, PR_LOG_DEBUG, args)
88 : #define LOG_ENABLED() PR_LOG_TEST(gFontDownloaderLog, PR_LOG_DEBUG)
89 :
90 :
91 0 : nsFontFaceLoader::nsFontFaceLoader(gfxProxyFontEntry *aProxy, nsIURI *aFontURI,
92 : nsUserFontSet *aFontSet, nsIChannel *aChannel)
93 : : mFontEntry(aProxy), mFontURI(aFontURI), mFontSet(aFontSet),
94 0 : mChannel(aChannel)
95 : {
96 0 : mFontFamily = aProxy->Family();
97 0 : }
98 :
99 0 : nsFontFaceLoader::~nsFontFaceLoader()
100 : {
101 0 : if (mLoadTimer) {
102 0 : mLoadTimer->Cancel();
103 0 : mLoadTimer = nsnull;
104 : }
105 0 : if (mFontSet) {
106 0 : mFontSet->RemoveLoader(this);
107 : }
108 0 : }
109 :
110 : void
111 0 : nsFontFaceLoader::StartedLoading(nsIStreamLoader *aStreamLoader)
112 : {
113 : PRInt32 loadTimeout =
114 0 : Preferences::GetInt("gfx.downloadable_fonts.fallback_delay", 3000);
115 0 : if (loadTimeout > 0) {
116 0 : mLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
117 0 : if (mLoadTimer) {
118 0 : mLoadTimer->InitWithFuncCallback(LoadTimerCallback,
119 : static_cast<void*>(this),
120 : loadTimeout,
121 0 : nsITimer::TYPE_ONE_SHOT);
122 : }
123 : } else {
124 0 : mFontEntry->mLoadingState = gfxProxyFontEntry::LOADING_SLOWLY;
125 : }
126 0 : mStreamLoader = aStreamLoader;
127 0 : }
128 :
129 : void
130 0 : nsFontFaceLoader::LoadTimerCallback(nsITimer *aTimer, void *aClosure)
131 : {
132 0 : nsFontFaceLoader *loader = static_cast<nsFontFaceLoader*>(aClosure);
133 :
134 0 : gfxProxyFontEntry *pe = loader->mFontEntry.get();
135 0 : bool updateUserFontSet = true;
136 :
137 : // If the entry is loading, check whether it's >75% done; if so,
138 : // we allow another timeout period before showing a fallback font.
139 0 : if (pe->mLoadingState == gfxProxyFontEntry::LOADING_STARTED) {
140 : PRInt32 contentLength;
141 : PRUint32 numBytesRead;
142 0 : if (NS_SUCCEEDED(loader->mChannel->GetContentLength(&contentLength)) &&
143 : contentLength > 0 &&
144 0 : NS_SUCCEEDED(loader->mStreamLoader->GetNumBytesRead(&numBytesRead)) &&
145 : numBytesRead > 3 * (PRUint32(contentLength) >> 2))
146 : {
147 : // More than 3/4 the data has been downloaded, so allow 50% extra
148 : // time and hope the remainder will arrive before the additional
149 : // time expires.
150 0 : pe->mLoadingState = gfxProxyFontEntry::LOADING_ALMOST_DONE;
151 : PRUint32 delay;
152 0 : loader->mLoadTimer->GetDelay(&delay);
153 0 : loader->mLoadTimer->InitWithFuncCallback(LoadTimerCallback,
154 : static_cast<void*>(loader),
155 : delay >> 1,
156 0 : nsITimer::TYPE_ONE_SHOT);
157 0 : updateUserFontSet = false;
158 0 : LOG(("fontdownloader (%p) 75%% done, resetting timer\n", loader));
159 : }
160 : }
161 :
162 : // If the font is not 75% loaded, or if we've already timed out once
163 : // before, we mark this entry as "loading slowly", so the fallback
164 : // font will be used in the meantime, and tell the context to refresh.
165 0 : if (updateUserFontSet) {
166 0 : pe->mLoadingState = gfxProxyFontEntry::LOADING_SLOWLY;
167 0 : nsPresContext *ctx = loader->mFontSet->GetPresContext();
168 0 : NS_ASSERTION(ctx, "fontSet doesn't have a presContext?");
169 : gfxUserFontSet *fontSet;
170 0 : if (ctx && (fontSet = ctx->GetUserFontSet()) != nsnull) {
171 0 : fontSet->IncrementGeneration();
172 0 : ctx->UserFontSetUpdated();
173 0 : LOG(("fontdownloader (%p) timeout reflow\n", loader));
174 : }
175 : }
176 0 : }
177 :
178 0 : NS_IMPL_ISUPPORTS1(nsFontFaceLoader, nsIStreamLoaderObserver)
179 :
180 : NS_IMETHODIMP
181 0 : nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
182 : nsISupports* aContext,
183 : nsresult aStatus,
184 : PRUint32 aStringLen,
185 : const PRUint8* aString)
186 : {
187 0 : if (!mFontSet) {
188 : // We've been canceled
189 0 : return aStatus;
190 : }
191 :
192 0 : mFontSet->RemoveLoader(this);
193 :
194 : #ifdef PR_LOGGING
195 0 : if (LOG_ENABLED()) {
196 0 : nsCAutoString fontURI;
197 0 : mFontURI->GetSpec(fontURI);
198 0 : if (NS_SUCCEEDED(aStatus)) {
199 0 : LOG(("fontdownloader (%p) download completed - font uri: (%s)\n",
200 : this, fontURI.get()));
201 : } else {
202 0 : LOG(("fontdownloader (%p) download failed - font uri: (%s) error: %8.8x\n",
203 : this, fontURI.get(), aStatus));
204 : }
205 : }
206 : #endif
207 :
208 0 : nsPresContext *ctx = mFontSet->GetPresContext();
209 0 : NS_ASSERTION(ctx && !ctx->PresShell()->IsDestroying(),
210 : "We should have been canceled already");
211 :
212 : // whether an error occurred or not, notify the user font set of the completion
213 0 : gfxUserFontSet *userFontSet = ctx->GetUserFontSet();
214 0 : if (!userFontSet) {
215 0 : return aStatus;
216 : }
217 :
218 0 : if (NS_SUCCEEDED(aStatus)) {
219 : // for HTTP requests, check whether the request _actually_ succeeded;
220 : // the "request status" in aStatus does not necessarily indicate this,
221 : // because HTTP responses such as 404 (Not Found) will still result in
222 : // a success code and potentially an HTML error page from the server
223 : // as the resulting data. We don't want to use that as a font.
224 0 : nsCOMPtr<nsIRequest> request;
225 0 : nsCOMPtr<nsIHttpChannel> httpChannel;
226 0 : aLoader->GetRequest(getter_AddRefs(request));
227 0 : httpChannel = do_QueryInterface(request);
228 0 : if (httpChannel) {
229 : bool succeeded;
230 0 : nsresult rv = httpChannel->GetRequestSucceeded(&succeeded);
231 0 : if (NS_SUCCEEDED(rv) && !succeeded) {
232 0 : aStatus = NS_ERROR_NOT_AVAILABLE;
233 : }
234 : }
235 : }
236 :
237 : // The userFontSet is responsible for freeing the downloaded data
238 : // (aString) when finished with it; the pointer is no longer valid
239 : // after OnLoadComplete returns.
240 : // This is called even in the case of a failed download (HTTP 404, etc),
241 : // as there may still be data to be freed (e.g. an error page),
242 : // and we need the fontSet to initiate loading the next source.
243 : bool fontUpdate = userFontSet->OnLoadComplete(mFontEntry,
244 : aString, aStringLen,
245 0 : aStatus);
246 :
247 : // when new font loaded, need to reflow
248 0 : if (fontUpdate) {
249 : // Update layout for the presence of the new font. Since this is
250 : // asynchronous, reflows will coalesce.
251 0 : ctx->UserFontSetUpdated();
252 0 : LOG(("fontdownloader (%p) reflow\n", this));
253 : }
254 :
255 0 : return NS_SUCCESS_ADOPTED_DATA;
256 : }
257 :
258 : void
259 0 : nsFontFaceLoader::Cancel()
260 : {
261 0 : mFontEntry->mLoadingState = gfxProxyFontEntry::NOT_LOADING;
262 0 : mFontSet = nsnull;
263 0 : if (mLoadTimer) {
264 0 : mLoadTimer->Cancel();
265 0 : mLoadTimer = nsnull;
266 : }
267 0 : mChannel->Cancel(NS_BINDING_ABORTED);
268 0 : }
269 :
270 : nsresult
271 0 : nsFontFaceLoader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
272 : nsIURI* aTargetURI,
273 : nsISupports* aContext)
274 : {
275 : nsresult rv;
276 :
277 0 : if (!aSourcePrincipal)
278 0 : return NS_OK;
279 :
280 : // check with the security manager
281 0 : nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
282 : rv = secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
283 0 : nsIScriptSecurityManager::STANDARD);
284 0 : if (NS_FAILED(rv)) {
285 0 : return rv;
286 : }
287 :
288 : // check content policy
289 0 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
290 : rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
291 : aTargetURI,
292 : aSourcePrincipal,
293 : aContext,
294 0 : EmptyCString(), // mime type
295 : nsnull,
296 : &shouldLoad,
297 : nsContentUtils::GetContentPolicy(),
298 0 : nsContentUtils::GetSecurityManager());
299 :
300 0 : if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
301 0 : return NS_ERROR_CONTENT_BLOCKED;
302 : }
303 :
304 0 : return NS_OK;
305 : }
306 :
307 0 : nsUserFontSet::nsUserFontSet(nsPresContext *aContext)
308 0 : : mPresContext(aContext)
309 : {
310 0 : NS_ASSERTION(mPresContext, "null context passed to nsUserFontSet");
311 0 : mLoaders.Init();
312 0 : }
313 :
314 0 : nsUserFontSet::~nsUserFontSet()
315 : {
316 0 : NS_ASSERTION(mLoaders.Count() == 0, "mLoaders should have been emptied");
317 0 : }
318 :
319 0 : static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
320 : void* aUserArg)
321 : {
322 0 : aKey->GetKey()->Cancel();
323 0 : return PL_DHASH_REMOVE;
324 : }
325 :
326 : void
327 0 : nsUserFontSet::Destroy()
328 : {
329 0 : mPresContext = nsnull;
330 0 : mLoaders.EnumerateEntries(DestroyIterator, nsnull);
331 0 : }
332 :
333 : void
334 0 : nsUserFontSet::RemoveLoader(nsFontFaceLoader *aLoader)
335 : {
336 0 : mLoaders.RemoveEntry(aLoader);
337 0 : }
338 :
339 : nsresult
340 0 : nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
341 : const gfxFontFaceSrc *aFontFaceSrc)
342 : {
343 : nsresult rv;
344 :
345 : // check same-site origin
346 0 : nsIPresShell *ps = mPresContext->PresShell();
347 0 : if (!ps)
348 0 : return NS_ERROR_FAILURE;
349 :
350 0 : NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal,
351 : "bad font face url passed to fontloader");
352 0 : NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
353 0 : if (!aFontFaceSrc->mURI)
354 0 : return NS_ERROR_FAILURE;
355 :
356 : // use document principal, original principal if flag set
357 : // this enables user stylesheets to load font files via
358 : // @font-face rules
359 0 : nsCOMPtr<nsIPrincipal> principal = ps->GetDocument()->NodePrincipal();
360 :
361 0 : NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
362 : "null origin principal in @font-face rule");
363 0 : if (aFontFaceSrc->mUseOriginPrincipal) {
364 0 : principal = do_QueryInterface(aFontFaceSrc->mOriginPrincipal);
365 : }
366 :
367 : rv = nsFontFaceLoader::CheckLoadAllowed(principal, aFontFaceSrc->mURI,
368 0 : ps->GetDocument());
369 0 : if (NS_FAILED(rv)) {
370 0 : LogMessage(aProxy, "download not allowed", nsIScriptError::errorFlag, rv);
371 0 : return rv;
372 : }
373 :
374 0 : nsCOMPtr<nsIStreamLoader> streamLoader;
375 0 : nsCOMPtr<nsILoadGroup> loadGroup(ps->GetDocument()->GetDocumentLoadGroup());
376 :
377 0 : nsCOMPtr<nsIChannel> channel;
378 : // get Content Security Policy from principal to pass into channel
379 0 : nsCOMPtr<nsIChannelPolicy> channelPolicy;
380 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
381 0 : rv = principal->GetCsp(getter_AddRefs(csp));
382 0 : NS_ENSURE_SUCCESS(rv, rv);
383 0 : if (csp) {
384 0 : channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
385 0 : channelPolicy->SetContentSecurityPolicy(csp);
386 0 : channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
387 : }
388 0 : rv = NS_NewChannel(getter_AddRefs(channel),
389 : aFontFaceSrc->mURI,
390 : nsnull,
391 : loadGroup,
392 : nsnull,
393 : nsIRequest::LOAD_NORMAL,
394 0 : channelPolicy);
395 :
396 0 : NS_ENSURE_SUCCESS(rv, rv);
397 :
398 : nsRefPtr<nsFontFaceLoader> fontLoader =
399 0 : new nsFontFaceLoader(aProxy, aFontFaceSrc->mURI, this, channel);
400 :
401 0 : if (!fontLoader)
402 0 : return NS_ERROR_OUT_OF_MEMORY;
403 :
404 : #ifdef PR_LOGGING
405 0 : if (LOG_ENABLED()) {
406 0 : nsCAutoString fontURI, referrerURI;
407 0 : aFontFaceSrc->mURI->GetSpec(fontURI);
408 0 : if (aFontFaceSrc->mReferrer)
409 0 : aFontFaceSrc->mReferrer->GetSpec(referrerURI);
410 0 : LOG(("fontdownloader (%p) download start - font uri: (%s) "
411 : "referrer uri: (%s)\n",
412 : fontLoader.get(), fontURI.get(), referrerURI.get()));
413 : }
414 : #endif
415 :
416 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
417 0 : if (httpChannel)
418 0 : httpChannel->SetReferrer(aFontFaceSrc->mReferrer);
419 0 : rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
420 0 : NS_ENSURE_SUCCESS(rv, rv);
421 :
422 0 : bool inherits = false;
423 : rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
424 : nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
425 0 : &inherits);
426 0 : if (NS_SUCCEEDED(rv) && inherits) {
427 : // allow data, javascript, etc URI's
428 0 : rv = channel->AsyncOpen(streamLoader, nsnull);
429 : } else {
430 : nsCOMPtr<nsIStreamListener> listener =
431 : new nsCORSListenerProxy(streamLoader, principal, channel,
432 0 : false, &rv);
433 0 : if (NS_FAILED(rv)) {
434 0 : fontLoader->DropChannel(); // explicitly need to break ref cycle
435 : }
436 0 : NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
437 0 : NS_ENSURE_SUCCESS(rv, rv);
438 :
439 0 : rv = channel->AsyncOpen(listener, nsnull);
440 : }
441 :
442 0 : if (NS_SUCCEEDED(rv)) {
443 0 : mLoaders.PutEntry(fontLoader);
444 0 : fontLoader->StartedLoading(streamLoader);
445 : }
446 :
447 0 : return rv;
448 : }
449 :
450 0 : static PLDHashOperator DetachFontEntries(const nsAString& aKey,
451 : nsRefPtr<gfxMixedFontFamily>& aFamily,
452 : void* aUserArg)
453 : {
454 0 : aFamily->DetachFontEntries();
455 0 : return PL_DHASH_NEXT;
456 : }
457 :
458 : bool
459 0 : nsUserFontSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
460 : {
461 0 : bool modified = false;
462 :
463 : // destroy any current loaders, as the entries they refer to
464 : // may be about to get replaced
465 0 : if (mLoaders.Count() > 0) {
466 0 : modified = true; // trigger reflow so that any necessary downloads
467 : // will be reinitiated
468 : }
469 0 : mLoaders.EnumerateEntries(DestroyIterator, nsnull);
470 :
471 0 : nsTArray<FontFaceRuleRecord> oldRules;
472 0 : mRules.SwapElements(oldRules);
473 :
474 : // destroy the font family records; we need to re-create them
475 : // because we might end up with faces in a different order,
476 : // even if they're the same font entries as before
477 0 : mFontFamilies.Enumerate(DetachFontEntries, nsnull);
478 0 : mFontFamilies.Clear();
479 :
480 0 : for (PRUint32 i = 0, i_end = aRules.Length(); i < i_end; ++i) {
481 : // insert each rule into our list, migrating old font entries if possible
482 : // rather than creating new ones; set modified to true if we detect
483 : // that rule ordering has changed, or if a new entry is created
484 0 : InsertRule(aRules[i].mRule, aRules[i].mSheetType, oldRules, modified);
485 : }
486 :
487 : // if any rules are left in the old list, note that the set has changed
488 0 : if (oldRules.Length() > 0) {
489 0 : modified = true;
490 : }
491 :
492 0 : if (modified) {
493 0 : IncrementGeneration();
494 : }
495 :
496 0 : return modified;
497 : }
498 :
499 : void
500 0 : nsUserFontSet::InsertRule(nsCSSFontFaceRule *aRule, PRUint8 aSheetType,
501 : nsTArray<FontFaceRuleRecord>& aOldRules,
502 : bool& aFontSetModified)
503 : {
504 0 : NS_ABORT_IF_FALSE(aRule->GetType() == mozilla::css::Rule::FONT_FACE_RULE,
505 : "InsertRule passed a non-fontface CSS rule");
506 :
507 : // set up family name
508 0 : nsAutoString fontfamily;
509 0 : nsCSSValue val;
510 : PRUint32 unit;
511 :
512 0 : aRule->GetDesc(eCSSFontDesc_Family, val);
513 0 : unit = val.GetUnit();
514 0 : if (unit == eCSSUnit_String) {
515 0 : val.GetStringValue(fontfamily);
516 : } else {
517 0 : NS_ASSERTION(unit == eCSSUnit_Null,
518 : "@font-face family name has unexpected unit");
519 : }
520 0 : if (fontfamily.IsEmpty()) {
521 : // If there is no family name, this rule cannot contribute a
522 : // usable font, so there is no point in processing it further.
523 : return;
524 : }
525 :
526 : // first, we check in oldRules; if the rule exists there, just move it
527 : // to the new rule list, and put the entry into the appropriate family
528 0 : for (PRUint32 i = 0; i < aOldRules.Length(); ++i) {
529 0 : const FontFaceRuleRecord& ruleRec = aOldRules[i];
530 0 : if (ruleRec.mContainer.mRule == aRule &&
531 : ruleRec.mContainer.mSheetType == aSheetType) {
532 0 : AddFontFace(fontfamily, ruleRec.mFontEntry);
533 0 : mRules.AppendElement(ruleRec);
534 0 : aOldRules.RemoveElementAt(i);
535 : // note the set has been modified if an old rule was skipped to find
536 : // this one - something has been dropped, or ordering changed
537 0 : if (i > 0) {
538 0 : aFontSetModified = true;
539 : }
540 : return;
541 : }
542 : }
543 :
544 : // this is a new rule:
545 :
546 0 : PRUint32 weight = NS_STYLE_FONT_WEIGHT_NORMAL;
547 0 : PRUint32 stretch = NS_STYLE_FONT_STRETCH_NORMAL;
548 0 : PRUint32 italicStyle = FONT_STYLE_NORMAL;
549 0 : nsString featureSettings, languageOverride;
550 :
551 : // set up weight
552 0 : aRule->GetDesc(eCSSFontDesc_Weight, val);
553 0 : unit = val.GetUnit();
554 0 : if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
555 0 : weight = val.GetIntValue();
556 0 : } else if (unit == eCSSUnit_Normal) {
557 0 : weight = NS_STYLE_FONT_WEIGHT_NORMAL;
558 : } else {
559 0 : NS_ASSERTION(unit == eCSSUnit_Null,
560 : "@font-face weight has unexpected unit");
561 : }
562 :
563 : // set up stretch
564 0 : aRule->GetDesc(eCSSFontDesc_Stretch, val);
565 0 : unit = val.GetUnit();
566 0 : if (unit == eCSSUnit_Enumerated) {
567 0 : stretch = val.GetIntValue();
568 0 : } else if (unit == eCSSUnit_Normal) {
569 0 : stretch = NS_STYLE_FONT_STRETCH_NORMAL;
570 : } else {
571 0 : NS_ASSERTION(unit == eCSSUnit_Null,
572 : "@font-face stretch has unexpected unit");
573 : }
574 :
575 : // set up font style
576 0 : aRule->GetDesc(eCSSFontDesc_Style, val);
577 0 : unit = val.GetUnit();
578 0 : if (unit == eCSSUnit_Enumerated) {
579 0 : italicStyle = val.GetIntValue();
580 0 : } else if (unit == eCSSUnit_Normal) {
581 0 : italicStyle = FONT_STYLE_NORMAL;
582 : } else {
583 0 : NS_ASSERTION(unit == eCSSUnit_Null,
584 : "@font-face style has unexpected unit");
585 : }
586 :
587 : // set up font features
588 0 : aRule->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
589 0 : unit = val.GetUnit();
590 0 : if (unit == eCSSUnit_Normal) {
591 : // empty feature string
592 0 : } else if (unit == eCSSUnit_String) {
593 0 : val.GetStringValue(featureSettings);
594 : } else {
595 0 : NS_ASSERTION(unit == eCSSUnit_Null,
596 : "@font-face font-feature-settings has unexpected unit");
597 : }
598 :
599 : // set up font language override
600 0 : aRule->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
601 0 : unit = val.GetUnit();
602 0 : if (unit == eCSSUnit_Normal) {
603 : // empty feature string
604 0 : } else if (unit == eCSSUnit_String) {
605 0 : val.GetStringValue(languageOverride);
606 : } else {
607 0 : NS_ASSERTION(unit == eCSSUnit_Null,
608 : "@font-face font-language-override has unexpected unit");
609 : }
610 :
611 : // set up src array
612 0 : nsTArray<gfxFontFaceSrc> srcArray;
613 :
614 0 : aRule->GetDesc(eCSSFontDesc_Src, val);
615 0 : unit = val.GetUnit();
616 0 : if (unit == eCSSUnit_Array) {
617 0 : nsCSSValue::Array *srcArr = val.GetArrayValue();
618 0 : size_t numSrc = srcArr->Count();
619 :
620 0 : for (size_t i = 0; i < numSrc; i++) {
621 0 : val = srcArr->Item(i);
622 0 : unit = val.GetUnit();
623 0 : gfxFontFaceSrc *face = srcArray.AppendElements(1);
624 0 : if (!face)
625 : return;
626 :
627 0 : switch (unit) {
628 :
629 : case eCSSUnit_Local_Font:
630 0 : val.GetStringValue(face->mLocalName);
631 0 : face->mIsLocal = true;
632 0 : face->mURI = nsnull;
633 0 : face->mFormatFlags = 0;
634 0 : break;
635 : case eCSSUnit_URL:
636 0 : face->mIsLocal = false;
637 0 : face->mURI = val.GetURLValue();
638 0 : NS_ASSERTION(face->mURI, "null url in @font-face rule");
639 0 : face->mReferrer = val.GetURLStructValue()->mReferrer;
640 0 : face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
641 0 : NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
642 :
643 : // agent and user stylesheets are treated slightly differently,
644 : // the same-site origin check and access control headers are
645 : // enforced against the sheet principal rather than the document
646 : // principal to allow user stylesheets to include @font-face rules
647 : face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
648 0 : aSheetType == nsStyleSet::eAgentSheet);
649 :
650 0 : face->mLocalName.Truncate();
651 0 : face->mFormatFlags = 0;
652 0 : while (i + 1 < numSrc && (val = srcArr->Item(i+1),
653 0 : val.GetUnit() == eCSSUnit_Font_Format)) {
654 0 : nsDependentString valueString(val.GetStringBufferValue());
655 0 : if (valueString.LowerCaseEqualsASCII("woff")) {
656 0 : face->mFormatFlags |= FLAG_FORMAT_WOFF;
657 0 : } else if (valueString.LowerCaseEqualsASCII("opentype")) {
658 0 : face->mFormatFlags |= FLAG_FORMAT_OPENTYPE;
659 0 : } else if (valueString.LowerCaseEqualsASCII("truetype")) {
660 0 : face->mFormatFlags |= FLAG_FORMAT_TRUETYPE;
661 0 : } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
662 0 : face->mFormatFlags |= FLAG_FORMAT_TRUETYPE_AAT;
663 0 : } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
664 0 : face->mFormatFlags |= FLAG_FORMAT_EOT;
665 0 : } else if (valueString.LowerCaseEqualsASCII("svg")) {
666 0 : face->mFormatFlags |= FLAG_FORMAT_SVG;
667 : } else {
668 : // unknown format specified, mark to distinguish from the
669 : // case where no format hints are specified
670 0 : face->mFormatFlags |= FLAG_FORMAT_UNKNOWN;
671 : }
672 0 : i++;
673 : }
674 0 : break;
675 : default:
676 0 : NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
677 : "strange unit type in font-face src array");
678 0 : break;
679 : }
680 : }
681 : } else {
682 0 : NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
683 : }
684 :
685 0 : if (srcArray.Length() > 0) {
686 0 : FontFaceRuleRecord ruleRec;
687 0 : ruleRec.mContainer.mRule = aRule;
688 0 : ruleRec.mContainer.mSheetType = aSheetType;
689 : ruleRec.mFontEntry = AddFontFace(fontfamily, srcArray,
690 : weight, stretch, italicStyle,
691 0 : featureSettings, languageOverride);
692 0 : if (ruleRec.mFontEntry) {
693 0 : mRules.AppendElement(ruleRec);
694 : }
695 : // this was a new rule and fontEntry, so note that the set was modified
696 0 : aFontSetModified = true;
697 : }
698 : }
699 :
700 : void
701 0 : nsUserFontSet::ReplaceFontEntry(gfxProxyFontEntry *aProxy,
702 : gfxFontEntry *aFontEntry)
703 : {
704 0 : for (PRUint32 i = 0; i < mRules.Length(); ++i) {
705 0 : if (mRules[i].mFontEntry == aProxy) {
706 0 : mRules[i].mFontEntry = aFontEntry;
707 0 : break;
708 : }
709 : }
710 : gfxMixedFontFamily *family =
711 0 : static_cast<gfxMixedFontFamily*>(aProxy->Family());
712 0 : if (family) {
713 0 : family->ReplaceFontEntry(aProxy, aFontEntry);
714 : }
715 0 : }
716 :
717 : nsCSSFontFaceRule*
718 0 : nsUserFontSet::FindRuleForEntry(gfxFontEntry *aFontEntry)
719 : {
720 0 : for (PRUint32 i = 0; i < mRules.Length(); ++i) {
721 0 : if (mRules[i].mFontEntry == aFontEntry) {
722 0 : return mRules[i].mContainer.mRule;
723 : }
724 : }
725 0 : return nsnull;
726 : }
727 :
728 : nsresult
729 0 : nsUserFontSet::LogMessage(gfxProxyFontEntry *aProxy,
730 : const char *aMessage,
731 : PRUint32 aFlags,
732 : nsresult aStatus)
733 : {
734 : nsCOMPtr<nsIConsoleService>
735 0 : console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
736 0 : if (!console) {
737 0 : return NS_ERROR_NOT_AVAILABLE;
738 : }
739 :
740 0 : NS_ConvertUTF16toUTF8 familyName(aProxy->FamilyName());
741 0 : nsCAutoString fontURI;
742 0 : if (aProxy->mSrcIndex == aProxy->mSrcList.Length()) {
743 0 : fontURI.AppendLiteral("(end of source list)");
744 : } else {
745 0 : if (aProxy->mSrcList[aProxy->mSrcIndex].mURI) {
746 0 : aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
747 : } else {
748 0 : fontURI.AppendLiteral("(invalid URI)");
749 : }
750 : }
751 :
752 : char weightKeywordBuf[8]; // plenty to sprintf() a PRUint16
753 : const char *weightKeyword;
754 : const nsAFlatCString& weightKeywordString =
755 0 : nsCSSProps::ValueToKeyword(aProxy->Weight(),
756 0 : nsCSSProps::kFontWeightKTable);
757 0 : if (weightKeywordString.Length() > 0) {
758 0 : weightKeyword = weightKeywordString.get();
759 : } else {
760 0 : sprintf(weightKeywordBuf, "%u", aProxy->Weight());
761 0 : weightKeyword = weightKeywordBuf;
762 : }
763 :
764 : nsPrintfCString
765 : msg(1024,
766 : "downloadable font: %s "
767 : "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
768 : aMessage,
769 : familyName.get(),
770 0 : aProxy->IsItalic() ? "italic" : "normal",
771 : weightKeyword,
772 0 : nsCSSProps::ValueToKeyword(aProxy->Stretch(),
773 0 : nsCSSProps::kFontStretchKTable).get(),
774 0 : aProxy->mSrcIndex);
775 :
776 0 : if (aStatus != 0) {
777 0 : msg.Append(": ");
778 0 : switch (aStatus) {
779 : case NS_ERROR_DOM_BAD_URI:
780 0 : msg.Append("bad URI or cross-site access not allowed");
781 0 : break;
782 : case NS_ERROR_CONTENT_BLOCKED:
783 0 : msg.Append("content blocked");
784 0 : break;
785 : default:
786 0 : msg.Append("status=");
787 0 : msg.AppendInt(aStatus);
788 0 : break;
789 : }
790 : }
791 0 : msg.Append("\nsource: ");
792 0 : msg.Append(fontURI);
793 :
794 : #ifdef PR_LOGGING
795 0 : if (PR_LOG_TEST(sUserFontsLog, PR_LOG_DEBUG)) {
796 0 : PR_LOG(sUserFontsLog, PR_LOG_DEBUG,
797 : ("userfonts (%p) %s", this, msg.get()));
798 : }
799 : #endif
800 :
801 : // try to give the user an indication of where the rule came from
802 0 : nsCSSFontFaceRule* rule = FindRuleForEntry(aProxy);
803 0 : nsString href;
804 0 : nsString text;
805 : nsresult rv;
806 0 : if (rule) {
807 0 : rv = rule->GetCssText(text);
808 0 : NS_ENSURE_SUCCESS(rv, rv);
809 0 : nsCOMPtr<nsIDOMCSSStyleSheet> sheet;
810 0 : rv = rule->GetParentStyleSheet(getter_AddRefs(sheet));
811 0 : NS_ENSURE_SUCCESS(rv, rv);
812 0 : rv = sheet->GetHref(href);
813 0 : NS_ENSURE_SUCCESS(rv, rv);
814 : }
815 :
816 : nsCOMPtr<nsIScriptError> scriptError =
817 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
818 0 : NS_ENSURE_SUCCESS(rv, rv);
819 :
820 0 : PRUint64 innerWindowID = GetPresContext()->Document()->InnerWindowID();
821 0 : rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(msg).get(),
822 : href.get(), // file
823 : text.get(), // src line
824 : 0, 0, // line & column number
825 : aFlags, // flags
826 : "CSS Loader", // category (make separate?)
827 0 : innerWindowID);
828 0 : if (NS_SUCCEEDED(rv)) {
829 0 : console->LogMessage(scriptError);
830 : }
831 :
832 0 : return NS_OK;
833 4392 : }
|