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

Brave Today on New Tab Page #6192

Merged
merged 46 commits into from
Nov 8, 2020
Merged

Brave Today on New Tab Page #6192

merged 46 commits into from
Nov 8, 2020

Conversation

petemill
Copy link
Member

@petemill petemill commented Jul 23, 2020

Resolves brave/brave-browser#12574

TODO

  • UI for "update available" and fetching action
  • Publisher name style fix
  • Automatic feed update every 1 hour
  • Automatic sources update every 1 day
  • Categorize sources in "customize" panel
  • Deep-link to customize sources on new "customize" button whilst scrolling Brave Today
  • Fix overall NTP container
  • Default pref to show Brave Today only if browser is set to English language.
  • Lazy-load data and code on NTP
  • Squash
  • Clear feed cache when user performs history clear
  • Clear data on "Reset sources to default" button'
  • fix lint
  • strings
  • declare new deps
  • cr87 TrustedTypes fixes
  • production urls
  • UI for no data
  • UI for loading state

Follow-up

  • Finish publisher title context-menu for disabling source
  • Consider not storing feed and publisher list in extension-process memory, only local storage. It's quite large.
  • p3a questions
  • japan feed
  • sponsored items
  • more unit tests
  • don't read feed cache to memory if user has previously turned on brave today but now has it turned off

Description

Implementing Brave Today on Desktop:

  • Fetching a single news feed from brave's "Private CDN" and displaying that feed on the NTP, mixing, choosing and paginating the feed accordingly. Using history data to score items based on whether the user has visited the article's Url host recently.
  • Fetching padded images for the feed from Private CDN and decoding them, passing them to the front-end as a "data URI".
  • Storing user preferences about which publishers (or sources) the user wishes to see feed items from and then performing that filtering on the device (in the browser).
  • Displaying feed on the front-end on the New Tab Page. Scrolling down shows the UI. This is enabled by default for English language users and opt-in (via NTP settings) for everyone else.
  • Adds Brave Today redux store including new action setup without constant strings and new async redux middleware.

Implementing a communication channel between WebUI (chrome://newtab) and the Brave Extension background page.

This is being used on this project for 2 reasons:

  1. Because SharedWorker is not (easily) supported in WebUI and I did not want to perform feed and image data manipulation on the front-end JS thread and for every single NTP instance (which are frequent).

  2. To perform a fetch of the feed, since NTP front-end is not able to make requests on the network.

Certainly, this could be performed in c++, but timing and the need to iterate meant that the background JS "worker" was a great place for this. This background fetching and prefs may well be ported to c++ in the future, most likely when working on bringing this feature to Android.

Storage / prefs

Aside from the regular usage of chromium profile prefs to determine if the user has opted-out if Language is set to English or opted-in for all other Languages, we are using extension (local) storage for user preferences as to which publisher sources to enable or disable for the Brave Today feed.

Architecture

Notable areas to consider:

  • Extension background process

    • today/index.ts
      Sets up listening to messages from the NTP WebUI (via chrome.runtime.onMessageExternal)
    • today/feed.ts.
      Fetches and caches (both in-memory and in local storage) remote data from private CDN.
    • today/publishers.ts
      Fetches and caches (both in-memory and in local storage) remote publisher list from private CDN.
    • today/publisher-user-prefs.ts
      Sets and gets prefs about which publishers are explicitly enabled or disabled by the user. Stores in chrome.storage.local.
    • today/feedToData.ts
      Content selection algorithm. Breaks the articles in to categories and pages. Somewhat randomizes and adds weight based on the user's recent browsing history.
    • today/privateCDN.ts
      Expresses URLs to fetch content from, and helps "unpad" consistent-byte-length images.
  • NTP WebUI renderer process

    • brave_new_tab_ui/async/today.ts
      Handles UI actions (init, refresh, preference-changed, etc) and communicates with extension background process, receiving feed data.
    • brave_new_tab_ui/components/default/braveToday/cards/CardImage.tsx
      Communicates with extension background process, receiving image data via padded content fetched from the Private CDN.

Submitter Checklist:

Test Plan:

Reviewer Checklist:

  • New files have MPL-2.0 license header.
  • Request a security/privacy review as needed.
  • Adequate test coverage exists to prevent regressions
  • Verify test plan is specified in PR before merging to source

After-merge Checklist:

  • The associated issue milestone is set to the smallest version that the
    changes has landed on.
  • All relevant documentation has been updated.

const pages: BraveToday.Page[] = []
let canGenerateAnotherPage = true
// Sanity check: arbitrary max pages so we don't end up
// in infinit loop.
Copy link
Member

Choose a reason for hiding this comment

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

typo: "infinite"

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed


export async function getUnpaddedAsDataUrl(buffer: ArrayBuffer, mimeType = 'image/jpg') {
const data = new DataView(buffer)
const contentLength = data.getUint32(0, false)
Copy link
Member

Choose a reason for hiding this comment

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

Might be helpful to use getUint32(0, false /* big endian */) to document that this is done correctly :)

Copy link
Member Author

Choose a reason for hiding this comment

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

I added that, thanks for the tip

// you can obtain one at http://mozilla.org/MPL/2.0/.

export const URLS = {
braveTodayFeed: 'https://pcdn.brave.software/brave-today/feed.json',
Copy link
Member

@fmarier fmarier Oct 27, 2020

Choose a reason for hiding this comment

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

This needs to be brave-today-cdn.brave.com.

Copy link
Member Author

Choose a reason for hiding this comment

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

Still waiting on this to be pushed to *.brave.com

"permissions": [ "contentSettings", "settingsPrivate", "management", "tabs", "storage", "webNavigation", "contextMenus", "cookies", "*://*/*", "chrome://favicon/*" ],
"content_security_policy": "default-src 'self'; font-src 'self' data:; script-src 'self'; style-src 'unsafe-inline'; img-src 'self' data: chrome://favicon/;",
"permissions": [ "contentSettings", "settingsPrivate", "management", "tabs", "history", "storage", "webNavigation", "contextMenus", "cookies", "*://*/*", "chrome://favicon/*", "https://pcdn.brave.software/*", "https://brave-today-cdn.brave.software/*" ],
"content_security_policy": "default-src 'self' https://pcdn.brave.software https://brave-today-cdn.brave.software; font-src 'self' data:; script-src 'self'; style-src 'unsafe-inline'; img-src 'self' data: chrome://favicon/ https://pcdn.brave.software; connect-src https://pcdn.brave.software https://brave-today-cdn.brave.software;",
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to avoid putting pcdn.brave.com and brave-today-cdn.brave.software in default-src and instead just put them in connect-src and img-src as needed.

Copy link
Member

Choose a reason for hiding this comment

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

are we going to use brave.software instead of brave.com for this service? if so we might need to do key pinning for it and also add it to the network audit whitelist if it's not there already

Copy link
Member

Choose a reason for hiding this comment

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

Actually I think brave-today-cdn.brave.software will also change to brave-today-cdn.brave.com before this is merged.

Copy link
Member Author

Choose a reason for hiding this comment

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

Still waiting for this to be deployed. It's coming soon as far as I can tell.

Copy link
Member

Choose a reason for hiding this comment

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

Just to recap, once the files have been uploaded to the right places, we can:

  • put pcdn.brave.com and brave-today-cdn.brave.com in connect-src and img-src
  • leave default-src set to 'self'

Copy link
Member Author

Choose a reason for hiding this comment

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

@fmarier I've put the staging URLS in connect-src and removed any additions to img-src and permissions. PTAL.

package.json Outdated Show resolved Hide resolved
@petemill petemill force-pushed the brave-today branch 3 times, most recently from 0f56cf0 to 2930db5 Compare October 29, 2020 05:23
@petemill petemill changed the title [wip] NTP: Brave Today Brave Today on New Tab Page Oct 29, 2020
}
}

export default async function getBraveTodayData (
Copy link
Member

Choose a reason for hiding this comment

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

Lots of logic in here - could be useful to break specific parts out to a new method(s) if we need to unit test specific parts. That could always be done at a later time (follow up) 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I was thinking I'd unit test this whole function. Already broken out take<T>, generateNextPage and the weighting, but can also break out all the category calculations.

@petemill petemill force-pushed the brave-today branch 3 times, most recently from 3f1b695 to 928b901 Compare October 29, 2020 18:46
@petemill petemill requested a review from ryanml October 29, 2020 21:17
Feed is once per hour and sources is once per day

Removed a unit test that was not doing anything useful (copying functionality from function to test and comparing output).
This shouldn't happen in production since the feed parser verifies that a publisher exists. Although it could happen if the sources list changes to remove a publisher, but the feed cache has not been updated yet. Also can occur in testing and storybook.
…drop-filter

Also, tighten style for indicator.
Overcomes a bug where a backdrop-filter element's ancestor which has a filter must also have a background. When this bug is fixed then this element won't need the background. Before this commit, the Widget title backgrounds would not correctly have backdrop filter because their ancestor had a blur and not also a background (it came from a higher up ancestor).
Deep links to correct Settings tab.
Clean up how we do deep links and refactor the "Add Cards" link too. In fact, cleaned up all tab index checking with an enum.
…fied

More Cards behavior and look should be the same
Creates a trusted types policy which is required now that we have strict trusted types enabled for scripts.
Prevents a bug where cmd-click an item would navigate to the article as well as the browser opening another tab.
- When Brave Today is interacted with (scrolled down to), get the current cached feed
- When NTP is opened and Today is enabled, make sure we have either loaded data from cache or fetched the first data feed (and publishers list)
- Whilst Brave Today is open, every 1 minutes, check if the backend has an updated feed, or if it knows that remote has updated feed. The backend will only check for remote update every 10 mins (whilst Brave Today is visible), via a HEAD request.
- Every 2 hours, the backend will do an actual feed refresh regardless of whether Brave Today is open or not
@petemill
Copy link
Member Author

petemill commented Nov 8, 2020

Pushed a very minor amend to fix a missing string and a bug that was preventing some deals from showing
https://github.com/brave/brave-core/compare/430287f635ef1517517c63df22c99dddc30ed7e5..01fe9f4e6d2129a35bc7295e23afbb51b9ef390d

...and also another rebase on master...

@bsclifton
Copy link
Member

There was one browser-test failure on Linux, but it appears to be intermittent as it passed great on prior runs:

22:40:40  1 test failed:
22:40:40      BraveShieldsAPIBrowserTest.AllowScriptsOnceIframe (../../brave/browser/extensions/api/brave_shields_api_browsertest.cc:185)

@@ -0,0 +1,46 @@
// Copyright (c) 2020 The Brave Authors. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

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

common has a specific meaning in chromium and this should not be at the top level of components like this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Brave Today news feed on the New Tab Page
8 participants