Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a fallback for font rendering when not allowed to use eval #6141

Merged
merged 1 commit into from
Aug 18, 2015

Conversation

skalnik
Copy link
Contributor

@skalnik skalnik commented Jun 24, 2015

This provides a fallback for font rendering if PDF.js is used in a place where it is not allowed to use eval, such as an environment with a CSP header with unsafe-eval.

It does so by changing the code emitted from the font_renderer to be more generic commands, and then when they're run in the font_loader, they're compiled into JS and used to construct a function like before. However if we catch an EvalError, we instead use Function.prototype.apply to run the commands.

Fixes #4660

@yurydelendik
Copy link
Contributor

Thanks for working on the issue. Could you address jshint issues and squash commits? Make sure you are friendly to the https://github.com/mozilla/pdf.js/wiki/Style-Guide.

Few points:

  • feature test ability to eval once and perform the transform for compiler or interpreter later
  • generate few less arrays to reduce amount of created object, e.g. use single array and add some smarts on the compiler/interpreter to scan it

@skalnik
Copy link
Contributor Author

skalnik commented Jun 25, 2015

@yurydelendik Thanks for the kind words! Its my pleasure to work on this 😁

I cleaned up much of the excess array creation, ensured I passed the linter, and applied the style guide rules where I saw I violated them. I'd love another review on this when you get a chance 👀

I'm unsure how to test this feature. I'm not the most familiar with this code base and I'm having trouble finding any tests around this. I also am not sure if there is a nice way to force the Function constructor to throw an EvalError instead of executing. If you had any pointers here they would be greatly appreciated 🙇

After this is resolved, I'm happy to squash my commits to prepare this for merging ✨

@timvandermeij
Copy link
Contributor

@skalnik It is easier for us to review your code if you squash the commits into one commit. Could you please do that? Thanks!

@skalnik skalnik force-pushed the fix-font-csp-issues branch from 6681cea to 2f7dd63 Compare June 26, 2015 15:23
@skalnik
Copy link
Contributor Author

skalnik commented Jun 26, 2015

@timvandermeij D'oh, I always just look at the PR diff on GitHub, so I didn't even think about that. I've gone ahead and squashed the commits 👍

}
/* jshint -W054 */
this.compiledGlyphs[character] = new Function('c', 'size', js);
} catch(e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use lazy initialized property (e.g. isEvalSupported) at FontLoader that will use shadow() where you will testnew Function capabilities and branch based on that, e.g. var result; try { new Function(''); result = true; } catch (e) { result = false }; return shadow(....

See example at https://github.com/skalnik/pdf.js/blob/fix-font-csp-issues/src/display/font_loader.js#L54

@yurydelendik
Copy link
Contributor

Reword commit comment "[PATCH] Add fallback for font loading when eval disabled
🔮" to "Add fallback for font loading when eval disabled"

@yurydelendik
Copy link
Contributor

I'm unsure how to test this feature. I'm not the most familiar with this code base and I'm having trouble finding any tests around this.

If you introduce FontLoader.isEvalSupported property, you will be able to fake different compile methods by returning true or false. The internal font rendering is working when PDFJS.disableFontFace is set to true.

@yurydelendik
Copy link
Contributor

Could you add isEvalSupported to the FontLoader?

  get isEvalSupported() {
    var result = false;
    try {
       new Function(''); // trying to compile a simple function
       result = true;
    } catch (e) {
      // eval is not supported
    }
   return shadow(this, 'isEvalSupported', result);
  },

And use it to branch the logic in the FontLoader.getPathGenerator. PDFJS.isEvalSupported check can be included in the function above as well.

@skalnik
Copy link
Contributor Author

skalnik commented Jun 30, 2015

Ah, after looking at your example, I think I understand what you were saying now. Sorry, a lot of these JS patterns are new to me :finnadie:

@timvandermeij
Copy link
Contributor

There are some lint errors. Tip: you can catch lint errors locally by running node make lint from a terminal. Please also keep the commits squashed for review. Thanks!

@Rob--W
Copy link
Member

Rob--W commented Jul 10, 2015

Could you optimize your code, by using objects instead of array & slice? Your current method involves a array copy operation at every glyph creation call. Here are some suggestions for optimization:

Use an object, not an array

I have created some alternatives and a benchmark, my proposed method ("usingObject") is 2-4x faster than your method (Safari 8. Chrome 43, Firefox 40,

  cmds.push({ name: 'lineTo',  args: [x, y]});  // usingObject
// instead of cmds.push(['lineTo', x, y]);      // usingSlice
// or even cmds.push(['lineTo', [x, y]]);       // usingArray

For a benchmark of the alternatives, see http://jsfiddle.net/5co62ohk/1/. My top suggestion is 3-4x faster than my proposed alternative in Chrome 43, Safari 8 and IE 11 and 1.3-1.5x faster in Firefox 40.

Special-case parameter-less arguments

The generic for loop in if (cmd === 'scale') is not needed, because scale always uses the same constant parameters. Instead of the for loop in this.compiledGlyphs[character], use the following. You could also put a comment at cmds.push({name: 'scale', args: []}); to clarify that scale is treated as a special case.

if (cmd === 'scale') {
    args = [size, -size];
}

@skalnik skalnik force-pushed the fix-font-csp-issues branch from 11aed83 to 8f044d6 Compare July 28, 2015 23:48
@skalnik
Copy link
Contributor Author

skalnik commented Jul 28, 2015

Could you optimize your code, by using objects instead of array & slice? Your current method involves a array copy operation at every glyph creation call. Here are some suggestions for optimization:

I've implemented these changes 😁

Oh wait! I lied, I didn't do the second one. Doing that now.

@skalnik skalnik force-pushed the fix-font-csp-issues branch 2 times, most recently from ac13648 to bf4544f Compare July 29, 2015 00:12
@skalnik
Copy link
Contributor Author

skalnik commented Aug 7, 2015

@Rob--W Any more thoughts on this?

@Rob--W
Copy link
Member

Rob--W commented Aug 7, 2015

@skalnik Two small notes, and then it looks fine to me.

@skalnik skalnik force-pushed the fix-font-csp-issues branch from bf4544f to ae25b9c Compare August 7, 2015 20:10
@skalnik
Copy link
Contributor Author

skalnik commented Aug 10, 2015

All fixed up 🔧

this.compiledGlyphs[character] = new Function('c', 'size', js);
var cmds = objs.get(this.loadedName + '_path_' + character);
var js = '';
var current, i, len, j, alen;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

j and alen don't seem to be used, so remove them.

And could you move var js = ''; inside if (FontLoader.isEvalSupported) {?

@Rob--W
Copy link
Member

Rob--W commented Aug 10, 2015

@skalnik I found another two minor issues. I guess that that's really it.

@skalnik skalnik force-pushed the fix-font-csp-issues branch 2 times, most recently from 81c395d to 88ba476 Compare August 12, 2015 16:24
@skalnik
Copy link
Contributor Author

skalnik commented Aug 12, 2015

Patched that up as well!

@Rob--W
Copy link
Member

Rob--W commented Aug 12, 2015

In terms of code, LGTM.

I'll defer the merge to @yurydelendik.

if (PDFJS.isEvalSupported) {
try {
/* jshint evil: true */
new Function('console.log("test");');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make function empty.

@yurydelendik
Copy link
Contributor

Looks good with comments above addressed.

@skalnik skalnik force-pushed the fix-font-csp-issues branch from 88ba476 to 04cbddc Compare August 12, 2015 21:10
@skalnik
Copy link
Contributor Author

skalnik commented Aug 12, 2015

Updated!

@timvandermeij
Copy link
Contributor

The bots time out when processing test/pdfs/glyph_accent.pdf with the following console message:

TypeError: current.args is undefined

@skalnik skalnik force-pushed the fix-font-csp-issues branch from 04cbddc to 8daceca Compare August 12, 2015 23:56
for (i = 0, len = cmds.length; i < len; i++) {
current = cmds[i];

if(current.args !== undefined) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: please add one space between if and (.

In some cases, such as in use with a CSP header, constructing a function with a
string of javascript is not allowed. However, compiling the various commands
that need to be done on the canvas element is faster than interpreting them.
This patch changes the font renderer to instead emit commands that are compiled
by the font loader. If, during compilation, we receive an EvalError, we instead
interpret them.
@skalnik skalnik force-pushed the fix-font-csp-issues branch from 8daceca to 341c5e9 Compare August 13, 2015 21:33
@skalnik
Copy link
Contributor Author

skalnik commented Aug 13, 2015

@timvandermeij That should be resolved. @Snuffleupagus tiny nits fixed up

@timvandermeij
Copy link
Contributor

/botio-linux preview

@pdfjsbot
Copy link

From: Bot.io (Linux)


Received

Command cmd_preview from @timvandermeij received. Current queue size: 0

Live output at: http://107.21.233.14:8877/a50cdef429e0d69/output.txt

@timvandermeij
Copy link
Contributor

/botio test

@pdfjsbot
Copy link

From: Bot.io (Linux)


Received

Command cmd_test from @timvandermeij received. Current queue size: 0

Live output at: http://107.21.233.14:8877/f72d3db853ecabb/output.txt

@pdfjsbot
Copy link

From: Bot.io (Windows)


Received

Command cmd_test from @timvandermeij received. Current queue size: 0

Live output at: http://107.22.172.223:8877/539e862e2ff05cb/output.txt

@pdfjsbot
Copy link

From: Bot.io (Windows)


Success

Full output at http://107.22.172.223:8877/539e862e2ff05cb/output.txt

Total script time: 18.47 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: Passed

@pdfjsbot
Copy link

From: Bot.io (Linux)


Success

Full output at http://107.21.233.14:8877/f72d3db853ecabb/output.txt

Total script time: 19.02 mins

  • Font tests: Passed
  • Unit tests: Passed
  • Regression tests: Passed

@timvandermeij
Copy link
Contributor

This patch looks really good and much cleaner than before to me. The tests also pass now. @yurydelendik would like to test this once more and after that this should be good to merge.

@skalnik
Copy link
Contributor Author

skalnik commented Aug 18, 2015

@yurydelendik any updates here since you wanted to test one more time?

@yurydelendik
Copy link
Contributor

Looks good. Thank you for the patch.

yurydelendik added a commit that referenced this pull request Aug 18, 2015
Provide a fallback for font rendering when not allowed to use `eval`
@yurydelendik yurydelendik merged commit c56dc9a into mozilla:master Aug 18, 2015
@skalnik skalnik deleted the fix-font-csp-issues branch July 26, 2016 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants